При разработке в клиент-серверной архитектуре платформы «1С:Предприятие 8.3» часто возникает задача пакетного формирования отчетов. Рассмотрим классическую ситуацию: нам необходимо на сервере сформировать несколько отчетов (например, оборотно-сальдовые ведомости по разным счетам), передать их на клиент и записать каждый отчет отдельным файлом в формате Excel.
Разберем по шагам, с какими трудностями можно столкнуться при реализации этой задачи, проанализируем популярное (но не всегда оптимальное) решение и выясним, как правильно организовать передачу файлов согласно современным стандартам разработки 1С.
Пользователю требуется сформировать отчеты в типовой конфигурации «Бухгалтерия предприятия 3.0». Изначально отчеты выводились на разные листы одной книги с помощью объекта ПакетОтображаемыхДокументов. Однако требования изменились: теперь каждый счет должен сохраняться в свой собственный файл с уникальным именем. Для передачи единичного файла разработчик использовал метод ПоместитьВоВременноеХранилище, но как быть, если файлов много?
Возникает вопрос: как связать сами данные файлов и их имена на сервере, чтобы корректно передать этот "пакет" на клиента и там сохранить?
Проанализируем одно из рабочих решений, к которому приходят разработчики. Идея заключается в том, чтобы на сервере сформировать массив, каждый элемент которого представляет собой структуру. В эту структуру помещается имя будущего файла и сам сформированный ТабличныйДокумент.
Посмотрим на пример кода, реализующий такой подход:
// В модуле внешней обработки
Функция СформироватьОСВНаСервере() Экспорт
МассивФайлов = Новый Массив;
// Формируем общую ОСВ
Если ВыводитьОСВ Тогда
Отчет = ПодготовитьОтчет("ОборотноСальдоваяВедомость");
ПараметрыОтчета = ПодготовитьПараметрыОтчетаОСВ(Отчет);
РезультатВыполненияОСВ = ПолучитьДанныеОтчета(Отчет, ПараметрыОтчета);
КнигаОСВ = РезультатВыполненияОСВ.Результат;
ИмяОСВ = "ОСВ_" + Формат(ТекущаяДата(), "ДФ=dd_MM_yyyy") + ".xls";
Книга = Новый Структура("ИмяФайла,ФайлОтчета", ИмяОСВ, КнигаОСВ);
МассивФайлов.Добавить(Книга);
КонецЕсли;
// ... (Цикл запроса по счетам для формирования отдельных отчетов)
// Внутри цикла:
// КнигаОСВПоСчету = РезультатВыполненияОСВПОСчету.Результат;
// ИмяОСВПоСчету = "ОСВ_" + РезультатЗапроса.НомерСчета + "_" + Формат(ТекущаяДата(),"ДФ=dd.MM.yyyy") + ".xls";
// Книга = Новый Структура("ИмяФайла,ФайлОтчета", ИмяОСВПоСчету, КнигаОСВПоСчету);
// МассивФайлов.Добавить(Книга);
Возврат МассивФайлов;
КонецФункции
// В модуле формы
&НаСервере
Функция ВыгрузитьНаСервере()
ОтчетОбъект = РеквизитФормыВЗначение("Объект");
Возврат ОтчетОбъект.СформироватьОСВНаСервере();
КонецФункции
&НаКлиенте
Процедура Выгрузить(Команда)
Если НЕ ЗначениеЗаполнено(Объект.ПутьККаталогу) Тогда
Сообщить("Не выбран каталог!");
Возврат;
Иначе
МассивДокументов = ВыгрузитьНаСервере();
Для Каждого Книга Из МассивДокументов Цикл
ПолныйПуть = Объект.ПутьККаталогу + "\" + Книга.ИмяФайла;
Книга.ФайлОтчета.Записать(ПолныйПуть, ТипФайлаТабличногоДокумента.XLS);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
В данном коде сервер возвращает массив, а на клиенте в цикле для каждого объекта ТабличныйДокумент вызывается метод Записать. Этот код выполнит задачу, файлы будут сохранены в указанный каталог. Но давайте разберем, какие архитектурные недостатки кроются в этом подходе.
Обратите внимание: в коде выше с сервера на клиент возвращается массив со сложными объектами платформы — табличными документами. При передаче контекста между сервером и клиентом 1С выполняет сериализацию данных.
Если отчеты содержат большое количество строк, оформлений и группировок, сериализация целого объекта ТабличныйДокумент приведет к следующим проблемам:
Как правильно делать: Табличные документы должны сохраняться во временные файлы формата Excel прямо на сервере. Затем эти файлы следует считывать в объект ДвоичныеДанные, помещать их во временное хранилище, а на клиент передавать только массив со строковыми адресами этих файлов и их желаемыми именами. На клиенте останется только получить файлы по их адресам.
Рассмотрим более современные и технологичные способы решения задачи, которые рекомендуется применять при разработке.
Если отчетов формируется действительно много (десятки или сотни файлов), передавать их по одному — значит плодить лишние вызовы и замедлять работу системы. Стандартом является упаковка всех сформированных отчетов в один архив на стороне сервера с помощью объекта ЗаписьZipФайла.
В этом случае вы помещаете во временное хранилище только один файл-архив. Клиент скачивает архив одним действием, что колоссально экономит трафик и время выполнения. После скачивания пользователь может самостоятельно извлечь файлы из архива в нужную ему папку.
В старых версиях кода можно встретить функцию ПолучитьФайл. Сегодня её использование считается устаревшим подходом (legacy), так как синхронные (модальные) вызовы блокируют работу интерфейса программы.
Для скачивания сразу нескольких файлов рекомендуется использовать асинхронные методы. Например, глобальную процедуру НачатьПолучениеФайловССервера. В нее передается массив структур, описывающих получаемые файлы (содержащий адрес во временном хранилище и предполагаемое имя файла). В версиях платформы 1С:Предприятие 8.3.18 и выше доступны методы, работающие через обещания (Promises), такие как ПолучитьФайлыССервераАсинх.
Так как наша задача решается в конфигурации «Бухгалтерия предприятия 3.0», мы можем значительно упростить себе жизнь, использовав готовый программный интерфейс БСП.
В конфигурации присутствуют общие модули ФайловаяСистемаКлиент и РаботаСФайламиКлиент. В них содержатся экспортные методы для работы с файлами. Эти процедуры уже умеют:
Если вы планируете, что пользователи будут формировать эти отчеты через браузер (в режиме веб-клиента), следует помнить о строгих правилах безопасности современных браузеров. Браузеры запрещают веб-страницам (в том числе и веб-клиенту 1С) «тихо» сохранять файлы в произвольные каталоги на диске пользователя (например, в C:\Temp\).
Код Книга.ФайлОтчета.Записать(ПолныйПуть, ТипФайлаТабличногоДокумента.XLS) в веб-клиенте выдаст ошибку.
Как решить эту проблему:
НачатьУстановкуРасширенияРаботыСФайлами или НачатьПодключениеРасширенияРаботыСФайламиЗавершение перед началом выгрузки.Подводя итог, при передаче файлов старайтесь минимизировать объем данных, курсирующих между клиентом и сервером, используйте двоичные данные вместо сложных объектов, архивируйте пакеты документов и обязательно применяйте асинхронные методы платформы 1С для обеспечения быстрой и стабильной работы приложения.