При интеграции 1С с внешними веб-системами или при разработке быстрых отчетов для пользователей часто возникает задача: передать ссылку на HTTP-сервис, при переходе по которой браузер формирует отчет и сразу предлагает пользователю сохранить или открыть файл (например, прайс-лист или выгрузку в формате Excel). Однако при простой отправке двоичных данных браузер либо отображает нечитаемые символы в окне, либо сохраняет файл с неверным расширением и некорректным именем.
Разберем подробно, почему так происходит, какие механизмы платформы 1С задействуются для передачи файлов и как правильно настроить HTTP-ответ, чтобы браузер корректно обработал выгрузку.
Когда веб-браузер отправляет стандартный GET-запрос к HTTP-сервису 1С, он ожидает в ответ не только двоичный поток байтов, но и управляющие метаданные — HTTP-заголовки. Если сервер возвращает тело файла без явного указания директив скачивания, браузер пытается самостоятельно определить формат и часто пытается отобразить содержимое как обычный текст или HTML-страницу.
Для корректного скачивания файла сервером должны быть выполнены два условия:
Content-Disposition со значением attachment и корректный тип содержимого Content-Type.Посмотрим на практический пример реализации метода GET в обработчике HTTP-сервиса. Предположим, что файл таблицы Excel уже сохранен во временный каталог на сервере.
Для формирования ответа используется объект HTTPСервисОтвет со статусом 200 (OK), а двоичные данные файла помещаются в тело ответа с помощью специального метода УстановитьТелоИзДвоичныхДанных():
Функция ВыгрузитьОтчетGET(Запрос)
// Формируем файл на сервере или получаем путь к существующему
ИмяФайла = КаталогВременныхФайлов() + "ПрайсЛист.xlsx";
ТабличныйДокумент.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
// Считываем бинарные данные
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
// Создаем объект ответа со статусом 200
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеФайла);
// Указываем браузеру, что файл нужно скачать, а не отображать
Ответ.Заголовки.Вставить("Content-Disposition", "attachment; filename=""Price.xlsx""");
Ответ.Заголовки.Вставить("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// Удаляем временный файл с диска
УдалитьФайлы(ИмяФайла);
Возврат Ответ;
КонецФункции
Разберем подробнее, что делают указанные строки:
Ответ.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеФайла) передает исходный бинарный поток в HTTP-ответ. Попытка использовать метод УстановитьТелоИзСтроки() для бинарных файлов приведет к искажению байтов и повреждению структуры файла.Content-Disposition: attachment предписывает браузеру открыть диалоговое окно скачивания файла, а параметр filename задает имя, под которым файл сохранится на компьютере клиента.Content-Type сообщает браузеру точный MIME-тип данных.Проанализируем распространенную ошибку: указание нестандартного типа вроде application/xlsx. Браузеры могут проигнорировать такой заголовок, что приведет к неверному сопоставлению программы для открытия.
Следует использовать официально зарегистрированные стандарты:
application/vnd.openxmlformats-officedocument.spreadsheetml.sheetapplication/vnd.ms-excelapplication/octet-streamЕсли требуется передать имя файла, содержащее русские буквы (например, Отчет_по_продажам.xlsx), простое использование функции КодироватьСтроку() внутри параметра filename приведет к тому, что браузер сохранит файл с закодированным именем (вида %D0%9E%D1%82%D1%87%D0%B5%D1%82.xlsx).
В соответствии со стандартами RFC 5987 и RFC 6266 для передачи нелатинских символов используется расширенный параметр filename* с указанием кодировки UTF-8. При этом для старых клиентов рекомендуется оставлять запасное латинское имя в параметре filename:
ИмяФайлаДляКлиента = "ОтчетПоПродажам.xlsx";
ИмяФайлаASCII = "Report.xlsx";
ЗакодированноеИмя = КодироватьСтроку(ИмяФайлаДляКлиента, СпособКодированияСтроки.КодировкаURL);
ЗаголовокDisposition = СтрШаблон(
"attachment; filename=""%1""; filename*=UTF-8''%2",
ИмяФайлаASCII,
ЗакодированноеИмя
);
Ответ.Заголовки.Вставить("Content-Disposition", ЗаголовокDisposition);
При регулярных обращениях к HTTP-сервису постоянная запись файлов на диск сервера и их последующее удаление создают лишнюю нагрузку на дисковую подсистему и могут приводить к засорению временного каталога при возникновении сбоев.
Начиная с платформы 1С:Предприятие 8.3.9, доступна выгрузка табличного документа напрямую в поток оперативной памяти через объект ПотокВПамяти. Рассмотрим этот оптимальный подход:
Функция СформироватьОтчетВПамятиGET(Запрос)
// Создаем и заполняем табличный документ
ТабДок = Новый ТабличныйДокумент;
// ... здесь выполняется вывод данных в ТабДок ...
// Записываем документ в поток памяти без обращения к диску
Поток = Новый ПотокВПамяти;
ТабДок.Записать(Поток, ТипФайлаТабличногоДокумента.XLSX);
ДвоичныеДанныеФайла = Поток.ЗакрытьИПолучитьДвоичныеДанные();
// Формируем HTTP-ответ
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеФайла);
// Заполняем обязательные и сервисные заголовки
Ответ.Заголовки.Вставить("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
Ответ.Заголовки.Вставить("Content-Disposition", "attachment; filename=""Report.xlsx""");
// Передаем точный размер для отображения шкалы прогресса в браузере
Ответ.Заголовки.Вставить("Content-Length", XMLСтрока(ДвоичныеДанныеФайла.Размер()));
// Запрещаем кэширование, чтобы отчет всегда содержал актуальные данные
Ответ.Заголовки.Вставить("Cache-Control", "no-cache, no-store, must-revalidate");
Ответ.Заголовки.Вставить("Pragma", "no-cache");
Ответ.Заголовки.Вставить("Expires", "0");
Возврат Ответ;
КонецФункции
Для обеспечения стабильного пользовательского опыта разберем назначение дополнительных заголовков:
XMLСтрока(ДвоичныеДанные.Размер()) позволяет браузеру рассчитать процент загрузки и показать пользователю корректный индикатор прогресса скачивания вместо бесконечного ожидания.При вызове HTTP-сервиса напрямую из адресной строки браузера необходимо учитывать два системных ограничения:
default.vrd) на веб-сервере необходимо настроить пул пользователей или жестко прописать пользователя 1С в параметрах сервиса.504 Gateway Timeout. Для тяжелых отчетов синхронную выгрузку использовать не рекомендуется — в таких сценариях отчет формируется в фоновом задании, а браузер забирает уже готовый файл отдельным запросом.