При интеграции 1С со сторонними приложениями программисты часто сталкиваются с ситуацией, когда внешний сервис возвращает файл (например, PDF) непосредственно в теле HTTP-ответа. Попытка обработать такой ответ как обычную строку текста — одна из самых распространенных ошибок, ведущая к повреждению данных. В этой статье мы подробно разберем причины возникновения проблемы и рассмотрим правильные методы сохранения бинарных данных в 1С.
Проанализируем типичную ситуацию: сервис возвращает данные, начинающиеся с сигнатуры %PDF-1.7. На первый взгляд кажется, что это текстовый формат, но на самом деле PDF является бинарным файлом. Попытка использовать объект ТекстовыйДокумент и метод УстановитьТекст() обречена на провал по нескольким причинам:
Рассмотрим, как правильно реализовать получение и сохранение файла, используя встроенные механизмы платформы.
Наиболее эффективным и правильным способом является сохранение данных напрямую на диск в момент выполнения запроса. Это избавляет нас от необходимости хранить тяжелые данные в оперативной памяти. Выясним, как это сделать с помощью метода Получить объекта HTTPСоединение.
Давайте изучим пример кода:
// Подготовим путь для сохранения файла
ИмяФайла = ПолучитьИмяВременногоФайла("pdf");
// Настроим соединение
Соединение = Новый HTTPСоединение("api.service.com", 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/get_invoice/123");
// Выполняем запрос и сразу указываем путь к выходному файлу
Ответ = Соединение.Получить(Запрос, ИмяФайла);
Если Ответ.КодСостояния = 200 Тогда
Сообщить("Файл успешно сохранен: " + ИмяФайла);
Иначе
Сообщить("Ошибка сервера: " + Ответ.КодСостояния);
КонецЕсли;
В данном случае платформа самостоятельно берет поток данных из сети и записывает его в файл ИмяФайла, минуя промежуточные преобразования в текст. Это самый надежный метод.
Иногда нам нужно сначала проанализировать ответ сервера (например, прочитать заголовки), а уже потом решить, сохранять файл или нет. В такой ситуации мы получаем тело ответа как объект ДвоичныеДанные. Рассмотрим этот алгоритм подробнее.
// Выполняем запрос без указания имени файла
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
// Получаем данные в бинарном виде
ДанныеФайла = Ответ.ПолучитьТелоКакДвоичныеДанные();
// Записываем данные на диск
ПутьКФайлу = "C:\Temp\Document.pdf";
ДанныеФайла.Записать(ПутьКФайлу);
Сообщить("Файл успешно записан.");
КонецЕсли;
Здесь крайне важно использовать метод ПолучитьТелоКакДвоичныеДанные() вместо метода ПолучитьТелоКакСтроку(). Это гарантирует сохранение каждого байта в исходном виде.
Бывают случаи, когда сторонний сервис возвращает не "чистый" PDF, а JSON-объект, в одном из полей которого находится файл. В таких случаях данные обычно закодированы в Base64. Проанализируем, как декодировать такие данные.
Допустим, мы получили строку ответа следующего вида:
{"status": "ok", "file_data": "JVBERi0xLjc..."}
Разберем по шагам, как извлечь из этого PDF:
// Предположим, ТелоОтвета - это строка JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ОтветСервера);
ДанныеJSON = ПрочитатьJSON(ЧтениеJSON);
// Извлекаем строку Base64
СтрокаBase64 = ДанныеJSON.file_data;
// Преобразуем Base64 обратно в двоичные данные
ДвоичныеДанныеФайла = Base64Значение(СтрокаBase64);
// Сохраняем
ДвоичныеДанныеФайла.Записать("C:\Files\Converted.pdf");
Важный момент: если строка Base64 слишком велика, работа со строковыми переменными может замедлить систему. В современных версиях платформы для этих целей рекомендуется использовать потоки.
Если предполагается работа с PDF-файлами большого объема (десятки и сотни мегабайт), загрузка их целиком в объект ДвоичныеДанные может привести к нехватке оперативной памяти. Давайте выясним, как использовать потоки, доступные начиная с версии 8.3.9.
Проанализируем ситуацию с использованием ПолучитьТелоКакПоток():
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
ПотокОтвета = Ответ.ПолучитьТелоКакПоток();
// Создаем поток для записи в файл
ПотокФайла = Новый ФайловыйПоток("C:\Data\LargeFile.pdf", РежимОткрытияФайла.Создать);
// Копируем данные из одного потока в другой порциями
ПотокОтвета.КопироватьВ(ПотокФайла);
// Закрываем потоки
ПотокФайла.Закрыть();
ПотокОтвета.Закрыть();
КонецЕсли;
Этот метод является наиболее профессиональным для высоконагруженных систем, так как он минимизирует использование ОЗУ.
Чтобы ваша интеграция была стабильной, рекомендуется придерживаться следующих правил:
Ответ.Заголовки.Получить("Content-Type"). Там должно быть значение application/pdf.ПолучитьИмяВременногоФайла(), чтобы избежать конфликтов доступа.Попытка...Исключение.ЗаписатьАсинх), чтобы не "фризить" интерфейс пользователя во время записи тяжелого файла на диск.Таким образом, мы выяснили, что ключевой секрет успеха — воспринимать PDF как набор байтов, а не как текст. Используя встроенные методы работы с двоичными данными и потоками, вы сможете реализовать надежную интеграцию с любым внешним сервисом.