Разберем типичную ситуацию: на стороне сервера в процессе выполнения кода (например, при выгрузке данных в CSV или формировании отчетов) создается несколько файлов. Логичным желанием разработчика является передать этот пакет файлов пользователю на локальный компьютер. Однако при попытке вернуть массив объектов типа Файл возникает ошибка: "Ошибка отображения типов: Отсутствует отображение для типа 'Файл'". В этой статье мы проанализируем, почему это происходит, и рассмотрим несколько эффективных способов решения задачи.
Для начала выясним причину проблемы. В системе 1С:Предприятие 8.3 объект Файл является программным интерфейсом для доступа к метаданным файла (его размеру, дате изменения, пути в файловой системе). Несмотря на то, что тип Файл доступен и на клиенте, и на сервере, он не является сериализуемым. Это означает, что платформа не может автоматически преобразовать этот объект в поток данных (XML или JSON) для передачи по каналу связи между сервером и тонким или веб-клиентом.
Более того, путь к файлу (например, "C:\Temp\file.csv"), существующий на сервере, чаще всего не имеет никакого смысла на стороне клиента. На сервере это локальный диск серверной машины, а клиент ожидает данные, которые он может сохранить у себя. Поэтому передавать нужно не объект Файл, а его содержимое в виде двоичных данных.
Самый простой и понятный способ — прочитать каждый созданный файл в объект ДвоичныеДанные, поместить их в массив или структуру и вернуть на клиент. Рассмотрим, как это реализовать.
На сервере выполним следующий код:
Функция ПолучитьФайлыНаСервере()
МассивРезультат = Новый Массив;
// Предположим, мы создали файлы в цикле
Для Счетчик = 1 По 5 Цикл
ИмяФайла = ПолучитьИмяВременногоФайла("csv");
// ... здесь логика записи текста в файл ...
// Читаем файл в ДвоичныеДанные
ДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
// Формируем структуру для передачи (чтобы сохранить имя файла)
СтруктураФайла = Новый Структура;
СтруктураФайла.Вставить("Имя", "Отчет_" + Счетчик + ".csv");
СтруктураФайла.Вставить("Данные", ДанныеФайла);
МассивРезультат.Добавить(СтруктураФайла);
КонецЦикла;
Возврат МассивРезультат;
КонецФункции
На клиенте мы получаем этот массив и сохраняем файлы по одному:
&НаКлиенте
Процедура ВыгрузитьФайлы()
СписокФайлов = ПолучитьФайлыНаСервере();
Для Каждого Элемент Из СписокФайлов Цикл
// Используем метод ПолучитьФайл (синхронный, для старых версий или толстого клиента)
// Или НачатьПолучениеФайла (для современных конфигураций)
Элемент.Данные.Записать(ПутьККаталогу + Элемент.Имя);
КонецЦикла;
КонецПроцедуры
Важно: этот метод плох тем, что все файлы передаются в одном "пакете" внутри XML/JSON ответа сервера. Если файлов много или они тяжелые, это может привести к перерасходу оперативной памяти или ошибке таймаута веб-сервера.
Если файлов много, гораздо эффективнее упаковать их в один архив прямо на сервере. Это решит проблему передачи множества объектов и значительно уменьшит объем трафика за счет сжатия текстовых данных (CSV сжимаются очень хорошо).
Проанализируем шаги реализации:
ДвоичныеДанные.
&НаСервере
Функция СформироватьАрхивНаСервере()
ИмяФайлаАрхива = ПолучитьИмяВременногоФайла("zip");
Архив = Новый ЗаписьZipФайла(ИмяФайлаАрхива);
// Добавляем файлы из временной папки сервера
Архив.Добавить("C:\Temp\Exchange\file1.csv");
Архив.Добавить("C:\Temp\Exchange\file2.csv");
Архив.Записать();
Возврат Новый ДвоичныеДанные(ИмяФайлаАрхива);
КонецФункции
В современных управляемых приложениях правильным тоном считается использование асинхронных методов платформы. Метод НачатьПолучениеФайлов позволяет скачать группу файлов максимально эффективно и без блокировки интерфейса.
Разберем по шагам, как это работает:
1. На сервере мы помещаем файлы во Временное хранилище и формируем массив объектов ОписаниеПередаваемогоФайла.
&НаСервере
Функция ПодготовитьФайлыДляСкачивания()
МассивОписаний = Новый Массив;
// Создаем файл и кладем его в хранилище
Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ПутьНаСервере), УникальныйИдентификатор);
// Формируем описание для платформы
Описание = Новый ОписаниеПередаваемогоФайла("Остатки.csv", Адрес);
МассивОписаний.Добавить(Описание);
Возврат МассивОписаний;
КонецФункции
2. На клиенте вызываем платформенный диалог получения файлов:
&НаКлиенте
Процедура ВыполнитьЗагрузку()
ПараметрыФайлов = ПодготовитьФайлыДляСкачивания();
// Платформа сама предложит пользователю выбрать папку (если нужно)
// и скачает все файлы из массива асинхронно
НачатьПолучениеФайлов(Новый ОписаниеОповещения("ЗавершитьЗагрузку", ЭтотОбъект), ПараметрыФайлов, , Истина);
КонецПроцедуры
Преимущества этого метода: он наиболее "родной" для веб-клиента. Браузеры часто блокируют скачивание нескольких файлов подряд, если они инициированы программно в цикле. Метод НачатьПолучениеФайлов обходит эти ограничения, так как платформа берет управление процессом на себя.
Если ваша конфигурация разработана на базе Библиотеки Стандартных Подсистем (БСП), не стоит изобретать велосипед. Проанализируем возможности модуля РаботаСФайламиКлиент. В нем существует метод СохранитьФайлы, который уже содержит в себе все необходимые проверки, обработки ошибок и асинхронные вызовы.
Использование БСП позволяет сократить ваш код до одной строки на клиенте, передав в процедуру массив с адресами временного хранилища и именами файлов. Это гарантирует стабильную работу программы как в тонком клиенте Windows, так и в браузере под Linux или macOS.
Подводя итог, проанализируем основные правила работы с файлами при передаче с сервера:
Файл в качестве результата серверной функции.ЗаписьZipФайла. Это сэкономит время пользователя и ресурсы сервера.Начать...), чтобы избежать зависания браузера.Соблюдение этих принципов позволит вам создавать надежные механизмы обмена данными, которые не будут "падать" с ошибкой отображения типов и обеспечат комфортную работу пользователей с системой. Для интерактивного обмена данными между каталогами подойдёт файловый менеджер для обмена файлами между клиентом и сервером.