Как настроить скачивание файла Excel в браузере при обращении к HTTP-сервису 1С?

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

При интеграции 1С с внешними веб-системами или при разработке быстрых отчетов для пользователей часто возникает задача: передать ссылку на HTTP-сервис, при переходе по которой браузер формирует отчет и сразу предлагает пользователю сохранить или открыть файл (например, прайс-лист или выгрузку в формате Excel). Однако при простой отправке двоичных данных браузер либо отображает нечитаемые символы в окне, либо сохраняет файл с неверным расширением и некорректным именем.

Разберем подробно, почему так происходит, какие механизмы платформы 1С задействуются для передачи файлов и как правильно настроить HTTP-ответ, чтобы браузер корректно обработал выгрузку.

Причина поведения браузера и механизм HTTP-сервиса

Когда веб-браузер отправляет стандартный GET-запрос к HTTP-сервису 1С, он ожидает в ответ не только двоичный поток байтов, но и управляющие метаданные — HTTP-заголовки. Если сервер возвращает тело файла без явного указания директив скачивания, браузер пытается самостоятельно определить формат и часто пытается отобразить содержимое как обычный текст или HTML-страницу.

Для корректного скачивания файла сервером должны быть выполнены два условия:

  1. Тело HTTP-ответа должно содержать сами бинарные данные файла.
  2. В заголовках ответа должны присутствовать директива 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");

    // Удаляем временный файл с диска
    УдалитьФайлы(ИмяФайла);

    Возврат Ответ;

КонецФункции

Разберем подробнее, что делают указанные строки:

  1. Метод Ответ.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеФайла) передает исходный бинарный поток в HTTP-ответ. Попытка использовать метод УстановитьТелоИзСтроки() для бинарных файлов приведет к искажению байтов и повреждению структуры файла.
  2. Заголовок Content-Disposition: attachment предписывает браузеру открыть диалоговое окно скачивания файла, а параметр filename задает имя, под которым файл сохранится на компьютере клиента.
  3. Заголовок Content-Type сообщает браузеру точный MIME-тип данных.

Корректные MIME-типы для файлов Excel

Проанализируем распространенную ошибку: указание нестандартного типа вроде application/xlsx. Браузеры могут проигнорировать такой заголовок, что приведет к неверному сопоставлению программы для открытия.

Следует использовать официально зарегистрированные стандарты:

  1. Для файлов формата Excel 2007 и выше (расширение .xlsx):
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  2. Для устаревшего формата Excel 97–2003 (расширение .xls):
    application/vnd.ms-excel
  3. Универсальный тип для принудительного скачивания произвольных бинарных файлов (включая архивы zip, pdf и др.):
    application/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");

    Возврат Ответ;

КонецФункции

Полезные дополнительные заголовки

Для обеспечения стабильного пользовательского опыта разберем назначение дополнительных заголовков:

  1. Content-Length: передача размера файла в байтах через XMLСтрока(ДвоичныеДанные.Размер()) позволяет браузеру рассчитать процент загрузки и показать пользователю корректный индикатор прогресса скачивания вместо бесконечного ожидания.
  2. Cache-Control: no-cache, no-store: критически важный заголовок для отчетов. Если его не передать, веб-сервер или браузер могут закэшировать первый скачанный файл. При повторном клике по ссылке пользователь получит старый отчет из кэша браузера без реального обращения к базе данных 1С.

Нюансы авторизации и таймаутов веб-сервера

При вызове HTTP-сервиса напрямую из адресной строки браузера необходимо учитывать два системных ограничения:

  1. Окно ввода логина и пароля: по умолчанию веб-сервер запросит Basic-аутентификацию. Чтобы файл скачивался анонимно, в файле публикации информационной базы (default.vrd) на веб-сервере необходимо настроить пул пользователей или жестко прописать пользователя 1С в параметрах сервиса.
  2. Таймаут формирования отчета: синхронный GET-запрос из браузера удерживает HTTP-соединение. Если формирование отчета занимает более 60–120 секунд, веб-сервер (Apache или IIS) разорвет соединение с ошибкой 504 Gateway Timeout. Для тяжелых отчетов синхронную выгрузку использовать не рекомендуется — в таких сценариях отчет формируется в фоновом задании, а браузер забирает уже готовый файл отдельным запросом.
← На главную