Как правильно сохранить PDF-файл, полученный от стороннего сервиса в 1С?

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

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

Почему нельзя использовать текстовый документ для PDF

Проанализируем типичную ситуацию: сервис возвращает данные, начинающиеся с сигнатуры %PDF-1.7. На первый взгляд кажется, что это текстовый формат, но на самом деле PDF является бинарным файлом. Попытка использовать объект ТекстовыйДокумент и метод УстановитьТекст() обречена на провал по нескольким причинам:

  1. Null-терминаторы: В бинарном потоке PDF-файла часто встречаются нулевые байты (символ с кодом 0). В языках программирования, таких как C (на которых базируется значительная часть системного кода), нулевой байт интерпретируется как признак конца строки. При попытке прочитать такие данные как текст, 1С может "обрезать" файл на первом же таком символе.
  2. Кодировки: При записи текста 1С применяет определенную кодировку (например, UTF-8 или UTF-16). Бинарные данные не соответствуют правилам кодирования текста, поэтому при преобразовании происходит подмена "непонятных" байтов на символы-заменители, что безвозвратно портит структуру PDF.
  3. Потеря структуры: PDF содержит метаданные и перекрестные ссылки, которые жестко привязаны к смещению байтов в файле. Любое изменение кодировки сдвигает эти данные, делая файл нечитаемым.

Рассмотрим, как правильно реализовать получение и сохранение файла, используя встроенные механизмы платформы.

Решение №1: Прямое сохранение в файл через HTTPСоединение

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

Давайте изучим пример кода:


// Подготовим путь для сохранения файла
ИмяФайла = ПолучитьИмяВременногоФайла("pdf");

// Настроим соединение
Соединение = Новый HTTPСоединение("api.service.com", 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
Запрос = Новый HTTPЗапрос("/get_invoice/123");

// Выполняем запрос и сразу указываем путь к выходному файлу
Ответ = Соединение.Получить(Запрос, ИмяФайла);

Если Ответ.КодСостояния = 200 Тогда
    Сообщить("Файл успешно сохранен: " + ИмяФайла);
Иначе
    Сообщить("Ошибка сервера: " + Ответ.КодСостояния);
КонецЕсли;

В данном случае платформа самостоятельно берет поток данных из сети и записывает его в файл ИмяФайла, минуя промежуточные преобразования в текст. Это самый надежный метод.

Решение №2: Использование объекта ДвоичныеДанные

Иногда нам нужно сначала проанализировать ответ сервера (например, прочитать заголовки), а уже потом решить, сохранять файл или нет. В такой ситуации мы получаем тело ответа как объект ДвоичныеДанные. Рассмотрим этот алгоритм подробнее.


// Выполняем запрос без указания имени файла
Ответ = Соединение.Получить(Запрос);

Если Ответ.КодСостояния = 200 Тогда
    // Получаем данные в бинарном виде
    ДанныеФайла = Ответ.ПолучитьТелоКакДвоичныеДанные();
    
    // Записываем данные на диск
    ПутьКФайлу = "C:\Temp\Document.pdf";
    ДанныеФайла.Записать(ПутьКФайлу);
    
    Сообщить("Файл успешно записан.");
КонецЕсли;

Здесь крайне важно использовать метод ПолучитьТелоКакДвоичныеДанные() вместо метода ПолучитьТелоКакСтроку(). Это гарантирует сохранение каждого байта в исходном виде.

Решение №3: Работа с Base64 (если данные внутри JSON)

Бывают случаи, когда сторонний сервис возвращает не "чистый" 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 слишком велика, работа со строковыми переменными может замедлить систему. В современных версиях платформы для этих целей рекомендуется использовать потоки.

Продвинутый подход: Использование потоков (Stream)

Если предполагается работа с PDF-файлами большого объема (десятки и сотни мегабайт), загрузка их целиком в объект ДвоичныеДанные может привести к нехватке оперативной памяти. Давайте выясним, как использовать потоки, доступные начиная с версии 8.3.9.

Проанализируем ситуацию с использованием ПолучитьТелоКакПоток():


Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
    ПотокОтвета = Ответ.ПолучитьТелоКакПоток();
    
    // Создаем поток для записи в файл
    ПотокФайла = Новый ФайловыйПоток("C:\Data\LargeFile.pdf", РежимОткрытияФайла.Создать);
    
    // Копируем данные из одного потока в другой порциями
    ПотокОтвета.КопироватьВ(ПотокФайла);
    
    // Закрываем потоки
    ПотокФайла.Закрыть();
    ПотокОтвета.Закрыть();
КонецЕсли;

Этот метод является наиболее профессиональным для высоконагруженных систем, так как он минимизирует использование ОЗУ.

Полезные советы при работе с внешними сервисами

Чтобы ваша интеграция была стабильной, рекомендуется придерживаться следующих правил:

Таким образом, мы выяснили, что ключевой секрет успеха — воспринимать PDF как набор байтов, а не как текст. Используя встроенные методы работы с двоичными данными и потоками, вы сможете реализовать надежную интеграцию с любым внешним сервисом.

← На главную