При обработке и загрузке объемных файлов Excel в табличные части документов пользователи часто сталкиваются с полной блокировкой интерфейса 1С:Предприятие. В это время программа перестает реагировать на действия пользователя, не позволяет открыть другие документы, справочники или отчеты. Разберем причины такого поведения платформы и детально рассмотрим архитектурные способы решения этой проблемы.
Выясним причину зависания интерфейса. Когда чтение файла и заполнение данных выполняются в контексте клиентского потока или через прямой синхронный вызов сервера &НаСервере, клиентское приложение переходит в режим ожидания ответа. Пока серверный или клиентский вызов не завершит обработку всех строк, управление пользователю не возвращается.
Попытки скрыть форму или изменить ее видимость программно не приносят результата: платформа все равно ожидает окончания выполнения запущенной процедуры. Кроме того, использование устаревшей технологии COM (объект COMОбъект("Excel.Application")) существенно замедляет процесс, так как инициализирует отдельный процесс операционной системы и работает строго синхронно.
Для решения задачи необходимо перенести тяжелые вычисления в асинхронный фоновый поток на стороне сервера.
Если конфигурация разработана на базе Библиотеки стандартных подсистем (например, «1С:Управление торговлей», «1С:ERP», «1С:Комплексная автоматизация» или «1С:Бухгалтерия предприятия»), правильным решением по стандартам разработки 1С является использование подсистемы Длительные операции.
Разберем по шагам алгоритм реализации неблокирующей загрузки через БСП:
ДлительныеОперации.ЗапуститьВыполнениеВФоне, передавая адрес файла во временном хранилище.ДлительныеОперацииКлиент.ОжидатьЗавершение или периодически проверяет статус задания через таймер, оставляя остальной интерфейс 1С полностью доступным.Посмотрим на пример клиентского кода запуска фоновой операции:
&НаКлиенте
Процедура ЗагрузитьИзExcel(Команда)
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Заголовок = "Выберите файл Excel для загрузки";
ПараметрыДиалога.Фильтр = "Файлы Excel (*.xlsx;*.xls)|*.xlsx;*.xls";
ОповещениеЗавершения = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайлаНаСервер(ОповещениеЗавершения, , , ПараметрыДиалога, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(ОписаниеПомещенногоФайла, ДополнительныеПараметры) Экспорт
Если ОписаниеПомещенногоФайла = Неопределено Тогда
Возврат;
КонецЕсли;
// Запускаем длительную операцию на сервере
ПараметрыПроцедуры = Новый Структура;
ПараметрыПроцедуры.Вставить("АдресФайла", ОписаниеПомещенногоФайла.Адрес);
ДлительнаяОперация = ЗапуститьФоновуюЗагрузку(ПараметрыПроцедуры);
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
ПараметрыОжидания.ТекстСообщения = "Выполняется чтение и обработка файла Excel...";
ПараметрыОжидания.ВыводитьПрогрессВыполнения = Истина;
ОповещениеОкончания = Новый ОписаниеОповещения("ПослеЗавершенияЗагрузки", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, ОповещениеОкончания, ПараметрыОжидания);
КонецПроцедуры
&НаСервере
Функция ЗапуститьФоновуюЗагрузку(ПараметрыПроцедуры)
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
ПараметрыВыполнения.НаименованиеФоновогоЗадания = "Загрузка данных из Excel";
Возврат ДлительныеОперации.ЗапуститьВыполнениеВФоне(
"ЗагрузкаДанныхСервер.ПрочитатьФайлExcel",
ПараметрыПроцедуры,
ПараметрыВыполнения
);
КонецФункции
Рассмотрим серверный метод фонового задания в общем модуле ЗагрузкаДанныхСервер:
Процедура ПрочитатьФайлExcel(ПараметрыПроцедуры, АдресРезультата) Экспорт
ДвоичныеДанные = ПолучитьИзВременногоХранилища(ПараметрыПроцедуры.АдресФайла);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
ДвоичныеДанные.Записать(ИмяВременногоФайла);
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
УдалитьФайлы(ИмяВременногоФайла);
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Номенклатура");
ТаблицаРезультат.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТаблицаРезультат.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
Для НомерСтроки = 2 По ТабДок.ВысотаТаблицы Цикл
НоваяСтрока = ТаблицаРезультат.Добавить();
НоваяСтрока.Номенклатура = ТабДок.Область(НомерСтроки, 1).Текст;
НоваяСтрока.Количество = Число(ТабДок.Область(НомерСтроки, 2).Текст);
НоваяСтрока.Цена = Число(ТабДок.Область(НомерСтроки, 3).Текст);
КонецЦикла;
ПоместитьВоВременноеХранилище(ТаблицаРезультат, АдресРезультата);
КонецПроцедуры
После получения сигнала об успешном завершении вызывается процедура оповещения, которая заполняет табличную часть:
&НаКлиенте
Процедура ПослеЗавершенияЗагрузки(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
Если Результат.Статус = "Выполнено" Тогда
ЗаполнитьТабличнуюЧастьИзХранилища(Результат.АдресРезультата);
ПоказатьОповещениеПользователя("Загрузка завершена", , "Данные успешно добавлены в документ.");
ИначеЕсли Результат.Статус = "Ошибка" Тогда
ВызватьИсключение Результат.ПодробноеПредставлениеОшибки;
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьТабличнуюЧастьИзХранилища(АдресРезультата)
ТаблицаДанных = ПолучитьИзВременногоХранилища(АдресРезультата);
Если ТипЗнч(ТаблицаДанных) = Тип("ТаблицаЗначений") Тогда
Объект.Товары.Загрузить(ТаблицаДанных);
КонецЕсли;
КонецПроцедуры
Если конфигурация является самописной или БСП не внедрена, применим прямой механизм платформы через объект ФоновыеЗадания.
Проанализируем ключевые особенности прямой работы с фоновыми заданиями:
ФоновыеЗадания.Выполнить() на сервере.ПодключитьОбработчикОжидания.Посмотрим на пример запуска фонового задания штатными средствами платформы:
&НаСервере
Функция ЗапуститьЗаданиеНапрямую(АдресФайла)
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(АдресФайла);
ПараметрыЗадания.Добавить(УникальныйИдентификатор);
Задание = ФоновыеЗадания.Выполнить("ЗагрузкаДанныхСервер.ВыполнитьФоновоеЧтение", ПараметрыЗадания, , "Фоновая загрузка");
Возврат Задание.УникальныйИдентификатор;
КонецФункции
Для дополнительного ускорения работы и снижения нагрузки на сервер рекомендуется отказаться от использования COM-объектов в пользу более быстрых инструментов:
ТабличныйДокумент.Прочитать() со способом чтения значений работает быстро, не требует установленного Microsoft Office и одинаково стабильно функционирует как под управлением Windows, так и на серверах Linux..xlsx представляет собой zip-архив с XML-документами. Прямое чтение файлов sharedStrings.xml и листов книги позволяет обрабатывать файлы размером в сотни тысяч строк за считанные секунды.Важные нюансы и ограничения: