При разработке внешних обработок или сложных отчетов часто возникает задача массового сохранения документов. Типичный пример — формирование сотни табличных документов и их экспорт в формат PDF на локальный диск пользователя. Основная проблема заключается в том, что клиентское приложение 1С является однопоточным. Если выполнять запись «в лоб» через цикл, интерфейс программы замирает, и пользователь не может продолжать работу, пока все файлы не будут обработаны. Рассмотрим подробнее, как решить эту проблему, используя современные механизмы платформы и БСП.
Прежде чем переходить к коду, разберем, почему процесс занимает много времени. Основная нагрузка ложится не на саму операцию записи на диск, а на процесс конвертации объекта ТабличныйДокумент в формат PDF. Этот процесс требует значительных вычислительных ресурсов процессора. Если выполнять его на стороне клиента, то весь сеанс пользователя блокируется до завершения последней итерации цикла.
Дополнительным фактором могут быть сетевые задержки, если запись производится не на локальный SSD, а на сетевой ресурс. В таких условиях время отклика файловой системы увеличивается кратно, что делает работу пользователя невозможной на протяжении нескольких минут.
Наиболее эффективный подход — делегировать тяжелую работу серверу. Мы можем запустить фоновое задание, которое подготовит все необходимые файлы, а клиент будет лишь периодически проверять статус готовности. Разберем этот алгоритм по шагам:
ДлительныеОперации.ВременноеХранилище.Проанализируем пример кода для запуска фонового задания:
// На клиенте
ПараметрыЗадания = Новый Структура("СписокДокументов", Объект.ДокументыКПечати);
РезультатЗадания = ДлительныеОперации.ЗапуститьВыполнениеВФоне(УникальныйИдентификатор,
"МойМодульСервер.СформироватьФайлыPDF", ПараметрыЗадания, "Подготовка файлов PDF");
// Запускаем ожидание через стандартные механизмы БСП
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(РезультатЗадания,
Новый ОписаниеОповещения("ЗавершитьПодготовкуФайлов", ЭтотОбъект), ПараметрыОжидания);
Такой подход позволяет пользователю не «висеть» на пустом экране, а видеть индикатор прогресса или даже продолжать работать в других окнах программы, если форма обработки открыта не модально.
Когда файлы на сервере готовы, возникает вопрос их передачи на клиент. Вместо того чтобы в цикле вызывать ПолучитьФайл (что порождает множество вызовов между сервером и клиентом), мы рекомендуем использовать метод ПолучитьФайлы. Это позволяет передать всю «пачку» данных за один сетевой запрос, что значительно быстрее в условиях медленного соединения.
Рассмотрим, как подготовить описание передаваемых файлов:
МассивФайловДляПередачи = Новый Массив;
Для Каждого СтрокаФайла Из ПодготовленныеДанные Цикл
// ОписаниеПередаваемогоФайла(ПутьНаКлиенте, АдресВоВременномХранилище)
Описание = Новый ОписаниеПередаваемогоФайла(СтрокаФайла.ИмяФайлаНаДиске, СтрокаФайла.АдресХранилища);
МассивФайловДляПередачи.Добавить(Описание);
КонецЦикла;
// Выполняем групповое получение без интерактивного диалога для каждого файла
ПолучитьФайлы(МассивФайловДляПередачи, , Ложь);
Использование этого метода оптимизирует трафик и минимизирует количество блокирующих операций на клиенте в момент передачи данных.
В современных версиях платформы (8.3.18+) 1С активно внедряет асинхронную модель. Если ваша конфигурация позволяет, вместо Записать() следует использовать ЗаписатьАсинхронно(). Это не ускорит конвертацию PDF как таковую, но позволит операционной системе и платформе управлять потоками более эффективно, не замораживая отрисовку интерфейса.
Пример использования асинхронного вызова для табличного документа:
// Пример для платформы 8.3.18+
Ждать ТабДок.ЗаписатьАсинхронно(ПутьКФайлу, ТипФайлаТабличногоДокумента.PDF);
Это делает код более чистым и современным, позволяя избежать устаревших конструкций с ОписаниеОповещения в простых сценариях.
Если файлов действительно очень много (тысячи), то даже пакетная передача может занять время. В этом случае рассмотрим вариант с созданием ZIP-архива на сервере. Мы упаковываем все PDF-файлы в один архив, передаем один файл на клиент и там же его распаковываем.
Выясним причину, почему это эффективно: во-первых, сжатие PDF (если внутри много текста) уменьшает объем передаваемых данных. Во-вторых, операционной системе гораздо проще скопировать один файл размером 100 Мб, чем 1000 файлов по 100 Кб, из-за особенностей работы файловых таблиц (MFT в NTFS).
Распаковку на клиенте можно выполнить как средствами встроенного объекта ЧтениеZipФайла, так и через запуск внешнего приложения (например, системного архиватора) в фоновом режиме, чтобы не блокировать 1С.
Иногда корень проблемы кроется в самом макете. Проанализируем ситуацию: если запись одного листа занимает 5 секунд, то 100 листов — это уже более 8 минут. Чтобы ускорить ТабличныйДокумент.Записать(..., PDF), придерживайтесь следующих правил:
Автомасштаб. Если оно включено, платформе приходится делать предварительный рендеринг для расчета коэффициента сжатия, что замедляет экспорт. Лучше заранее рассчитать ширину колонок под формат A4.При массовой записи файлов на диск часто срабатывает монитор антивируса, который проверяет каждый новый PDF-файл. Это может замедлить процесс в несколько раз. Если вы столкнулись с аномальными тормозами именно в момент записи на клиенте, попробуйте временно отключить антивирус для теста. В качестве технического решения можно записывать файлы в специализированную временную директорию, которая добавлена в исключения антивирусного ПО, а затем перемещать их в целевую папку.
Подводя итог, можно сказать, что «золотой стандарт» решения данной задачи — это подготовка данных в фоновом задании на сервере с последующей пакетной передачей через ПолучитьФайлы. Это обеспечивает наилучший пользовательский опыт и стабильную работу системы.