Как правильно сохранить произвольные данные из HTTP-ответа в файл в 1С

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

При работе с внешними API и веб-сервисами программисты 1С часто сталкиваются с задачей получения файлов: это могут быть счета в формате PDF, электронные документы XML, изображения или архивы. Распространенной ошибкой является попытка считать тело ответа как обычную строку. В этой статье мы подробно разберем, почему текстовые методы не подходят для бинарных данных, и рассмотрим наиболее надежные способы сохранения файлов без искажений.

Почему нельзя использовать ПолучитьТелоКакСтроку() для PDF и бинарных данных

Рассмотрим ситуацию, когда сервис возвращает содержимое файла. Если мы используем метод HTTPОтвет.ПолучитьТелоКакСтроку(), платформа пытается интерпретировать последовательность байтов как символы определенной кодировки (например, UTF-8 или Windows-1251). Для текстовых файлов (XML, TXT) это может сработать, но для PDF, PNG или ZIP это губительно. Бинарные файлы содержат байты, которые не имеют строкового представления в стандартных таблицах символов. При попытке конвертации такие байты либо заменяются «символами-заглушками», либо обрезают строку, если встречается символ конца строки в неожиданном месте. В результате файл получается «битым» и не открывается целевым приложением.

Способ 1: Получение данных через объект ДвоичныеДанные

Если нам необходимо обработать данные в оперативной памяти перед записью (например, поместить их во временное хранилище для передачи на клиент), мы используем метод ПолучитьТелоКакДвоичныеДанные(). Проанализируем пример кода на стороне сервера:


&НаСервере
Функция ПолучитьФайлИзСервиса(ПараметрыЗапроса)
    
    Соединение = Новый HTTPСоединение("api.service.ru", , , , , 30, Новый ЗащищенноеСоединениеOpenSSL());
    Запрос = Новый HTTPЗапрос("/get_file?id=" + ПараметрыЗапроса.Идентификатор);
    
    Ответ = Соединение.Получить(Запрос);
    
    Если Ответ.КодСостояния = 200 Тогда
        // Получаем «сырые» байты без привязки к кодировке текста
        ДанныеФайла = Ответ.ПолучитьТелоКакДвоичныеДанные();
        
        // Помещаем во временное хранилище, чтобы передать на клиент
        АдресХранилища = ПоместитьВоВременноеХранилище(ДанныеФайла);
        Возврат АдресХранилища;
    Иначе
        ВызватьИсключение "Ошибка сервиса: " + Ответ.КодСостояния;
    КонецЕсли;
    
КонецФункции

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


&НаКлиенте
Процедура СохранитьФайл(АдресХранилища)
    
    Данные = ПолучитьИзВременногоХранилища(АдресХранилища);
    // Важно: расширение должно соответствовать типу контента
    ПутьКФайлу = "C:\Downloads\document.pdf"; 
    Данные.Записать(ПутьКФайлу);
    
    Сообщить("Файл успешно сохранен: " + ПутьКФайлу);
    
КонецПроцедуры

Важный нюанс: Если при открытии сохраненного файла вы видите текстовое представление HEX-кодов (например, 3C 3F 78 6D 6C...), это означает, что сам сервер присылает данные в текстовом формате HEX или Base64. В этом случае перед записью данные необходимо декодировать методами Base64Значение() или парсингом HEX-строки.

Способ 2: Прямая запись в файл (самый надежный метод)

Наиболее эффективный и безопасный с точки зрения потребления памяти способ — это использование второго параметра метода Получить() объекта HTTPСоединение. В этом случае платформа 1С записывает поток данных из HTTP-ответа напрямую на диск, минуя промежуточные объекты в памяти.

Рассмотрим, как реализовать этот подход. Мы создадим временный файл на сервере, запишем туда ответ, а затем передадим результат пользователю.


&НаСервере
Функция СкачатьФайлНапрямую()
    
    Соединение = Новый HTTPСоединение("example.com");
    Запрос = Новый HTTPЗапрос("/download/report.pdf");
    
    // Генерируем уникальное имя временного файла
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("pdf");
    
    // Указываем путь к файлу как второй параметр метода Получить
    Ответ = Соединение.Получить(Запрос, ИмяВременногоФайла);
    
    Если Ответ.КодСостояния = 200 Тогда
        // Файл уже записан на диск платформой
        Данные = Новый ДвоичныеДанные(ИмяВременногоФайла);
        Адрес = ПоместитьВоВременноеХранилище(Данные);
        
        // Удаляем временный файл после помещения в хранилище
        УдалитьФайлы(ИмяВременногоФайла);
        
        Возврат Адрес;
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Этот метод исключает любые проблемы с кодировками, так как 1С работает с ответом как с FileStream. Если сервер вернул файл, он будет сохранен байт-в-байт в первозданном виде.

Как определить имя и расширение файла из заголовков

Часто мы не знаем заранее, файл какого типа прилетит (PDF или XML). В этом случае проанализируем заголовки HTTP-ответа. Обычно сервер передает информацию в заголовке Content-Type (MIME-тип) или Content-Disposition (рекомендуемое имя файла).

Разберем пример извлечения имени файла:


// В объекте HTTPОтвет есть соответствие Заголовки
ИмяФайлаСервера = Ответ.Заголовки.Получить("Content-Disposition");
// Пример значения: attachment; filename="report_2023.pdf"

Если ИмяФайлаСервера <> Неопределено Тогда
    // Простая очистка строки для получения только имени
    ЧастиИмени = СтрРазделить(ИмяФайлаСервера, "=");
    Если ЧастиИмени.Количество() > 1 Тогда
        ЧистоеИмя = СтрЗаменить(ЧастиИмени[1], """", "");
        Сообщить("Сервер предлагает имя: " + ЧистоеИмя);
    КонецЕсли;
КонецЕсли;

Подведем итоги

Для корректной работы с произвольными данными в 1С при использовании HTTPСоединение следует придерживаться следующих правил:

  1. Никогда не используйте текстовые методы для работы с нетекстовыми данными (PDF, изображения, исполняемые файлы).
  2. Для передачи данных между сервером и клиентом используйте ДвоичныеДанные и ВременноеХранилище.
  3. Метод Соединение.Получить(Запрос, ИмяВыходногоФайла) — самый стабильный вариант для сохранения файлов большого объема, так как он не перегружает оперативную память сервера.
  4. Всегда проверяйте заголовки ответа Content-Type, чтобы понимать, в каком формате пришли данные (binary, base64 или hex).

Используя эти подходы, вы обеспечите стабильную работу своих интеграций и корректное открытие файлов у конечных пользователей.

← На главную