Как правильно организовать массовую загрузку и подписание файлов на сервере в управляемых формах 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с большими объемами данных, например, когда необходимо обработать более 30 000 документов (Реализаций товаров и услуг), сформировать для них печатные формы, подписать их на стороне клиента локальным сертификатом и передать обратно на сервер, разработчики часто сталкиваются с проблемой асинхронности. В управляемом приложении 1С методы для работы с файлами работают в асинхронном режиме. Это приводит к тому, что основной код процедуры завершается до того, как файлы фактически будут переданы на сервер. В результате форма разблокируется, пользователь может ее закрыть, а процесс загрузки прервется или приведет к ошибкам. В данной статье мы подробно разберем, как решить эту задачу, обеспечив стабильность и контроль процесса.

Проблема асинхронности и блокировка интерфейса

Рассмотрим стандартную ситуацию: мы используем метод НачатьПомещениеФайлаНаСервер. Поскольку этот метод не останавливает выполнение основного потока кода, процедура, инициировавшая загрузку, завершается мгновенно. Пользователь видит доступную форму и может совершить действия, которые нарушат логику обработки. Для предотвращения таких ситуаций нам необходимо внедрить механизм контроля состояния формы.

Проанализируем решение через использование переменной-флага. В модуле формы мы определим реквизит (или переменную) ПроцессЗагрузкиИдет типа Булево. Перед началом асинхронной операции мы устанавливаем это значение в Истина, а в обработчике завершения — в Ложь.

Для того чтобы пользователь не закрыл форму в процессе, мы переопределим обработчик события ПередЗакрытием на клиенте:


&НаКлиенте
Процедура ПередЗакрытием(Отказ, СтандартнаяОбработка)
    Если ПроцессЗагрузкиИдет Тогда
        Отказ = Истина;
        ПоказатьПредупреждение(, "Пожалуйста, дождитесь завершения загрузки и обработки файлов!");
    КонецЕсли;
КонецПроцедуры

Разберем, как это работает вместе с методом загрузки. Когда мы вызываем НачатьПомещениеФайловНаСервер, мы передаем в него ОписаниеОповещения. Только в процедуре, указанной в этом описании, мы сбросим наш флаг блокировки.

Организация очереди вместо бесконечного цикла

Одной из главных ошибок при массовой обработке является попытка запустить цикл по всем 30 000 файлам сразу. Это создает огромную нагрузку на сетевой стек и может привести к переполнению стека вызовов. Вместо этого мы рекомендуем использовать алгоритм "Очереди блоков".

Выясним причину, почему блоки так важны. При передаче файлов через временное хранилище, данные занимают оперативную память рабочего процесса rphost. Передача 3 ГБ данных одним пакетом может привести к падению сервера или критическому замедлению системы. Разделим наши 30 000 документов на блоки, например, по 500 или 1000 штук.

Рассмотрим структуру алгоритма обработки блока:

  1. Выделяем массив ссылок на документы для текущего блока.
  2. На сервере формируем печатные формы и возвращаем их на клиент (адреса во временном хранилище).
  3. На клиенте подписываем файлы, используя объект МенеджерКриптографии.
  4. Вызываем асинхронное помещение подписанных файлов на сервер.
  5. В процедуре завершения (callback) инициируем начало обработки следующего блока.

Посмотрим на пример кода, который реализует последовательную обработку блоков без рекурсии через обработчик ожидания:


&НаКлиенте
Перем ТекущийБлок;

&НаКлиенте
Процедура НачатьОбработкуОчереди()
    ПроцессЗагрузкиИдет = Истина;
    ТекущийБлок = 1;
    ВыполнитьОбработкуТекущегоБлока();
КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьОбработкуТекущегоБлока()
    // Логика подготовки файлов блока
    // ...
    ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗагрузкиБлока", ЭтотОбъект);
    // Помещаем файлы текущего блока на сервер
    НачатьПомещениеФайловНаСервер(ОписаниеОповещения, , , , СписокФайлов, УникальныйИдентификатор);
КонецПроцедуры

&НаКлиенте
Процедура ПослеЗагрузкиБлока(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = Неопределено Тогда
        ПроцессЗагрузкиИдет = Ложь;
        Возврат;
    КонецЕсли;
    
    // Вызываем серверный метод для обработки загруженных файлов
    ОбработатьФайлыНаСервере(ТекущийБлок);
    
    // Проверяем, есть ли еще блоки
    Если ЕстьЕщеДанные(ТекущийБлок) Тогда
        ТекущийБлок = ТекущийБлок + 1;
        // Используем небольшую паузу, чтобы очистить стек и дать интерфейсу "вздохнуть"
        ПодключитьОбработчикОжидания("ВыполнитьОбработкуТекущегоБлока", 0.1, Истина);
    Иначе
        ПроцессЗагрузкиИдет = Ложь;
        Сообщить("Все документы успешно обработаны!");
    КонецЕсли;
КонецПроцедуры

Поиск и сопоставление данных на сервере

После того как блок файлов попал на сервер, нам нужно связать их с исходными документами в табличной части формы. Проанализируем ситуацию: в табличной части 30 000 строк. Использовать метод НайтиСтроки для каждой ссылки в цикле может быть накладно, но это наиболее штатный путь для управляемых форм.

Для оптимизации мы можем использовать колонку НомерБлока. Рассмотрим серверный метод обработки:


&НаСервере
Процедура ОбработатьФайлыНаСервере(НомерБлока)
    ПараметрыОтбора = Новый Структура("НомерБлока", НомерБлока);
    НайденныеСтроки = Объект.ДокументыКОбработке.НайтиСтроки(ПараметрыОтбора);
    
    Для Каждого СтрокаТЧ Из НайденныеСтроки Цикл
        // Создаем связанный объект ЭДО
        НовыйЭДО = Документы.ЭлектронныйДокумент.СоздатьДокумент();
        // Заполняем реквизиты и прикрепляем файл из временного хранилища
        // ...
        НовыйЭДО.Записать();
        
        // Сохраняем ссылку в ТЧ для отображения пользователю
        СтрокаТЧ.СсылкаЭДО = НовыйЭДО.Ссылка;
    КонецЦикла;
КонецПроцедуры

Технические нюансы при работе с большими объемами

При реализации данного механизма крайне важно учитывать следующие аспекты, которые помогут избежать падения системы:

  1. Очистка временного хранилища: После того как файл из временного хранилища был записан в базу (в справочник или присоединенный файл), адрес в хранилище больше не нужен. При использовании УникальныйИдентификаторФормы данные удалятся только при закрытии формы. Для 30 000 файлов это может привести к нехватке памяти. Рекомендуется использовать "короткое" хранилище или принудительно очищать объекты после обработки блока.
  2. Лимиты Веб-сервера: Если вы используете тонкий клиент через HTTP или веб-клиент, проверьте настройки MaxAllowedContentLength в IIS или LimitRequestBody в Apache. Если пакет из 2000 PDF-файлов превысит эти лимиты, загрузка оборвется с ошибкой соединения.
  3. Работа криптографии: Подписание файлов — ресурсоемкая операция. Если вы подписываете их в основном потоке клиента, интерфейс 1С может "замереть". Использование цепочки ОписаниеОповещения частично решает эту проблему, давая приложению возможность обрабатывать сообщения очереди между блоками.
  4. БСП "Длительные операции": Если в вашей конфигурации используется Библиотека Стандартных Подсистем (БСП), мы рекомендуем использовать подсистему ДлительныеОперации. Она позволяет вывести штатное окно прогресса с кнопкой "Отмена" и индикатором выполнения (например, "Обработано 1500 из 30000"). Это выглядит более профессионально и избавляет от ручного управления флагами блокировки.

Подводя итог, можно сказать, что ключом к успешной загрузке огромного массива файлов является разделение на блоки, разрыв цепочки вызовов через обработчик ожидания и строгий контроль состояния интерфейса через события формы. Такой подход гарантирует, что даже при слабом интернет-соединении или высокой загрузке сервера все документы будут подписаны и обработаны корректно.

← На главную