Как получить и сохранить PDF-документ из строки Base64, полученной от веб-сервиса в 1С

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

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

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

Базовый механизм преобразования строки Base64 в файл PDF

Для обратного преобразования закодированной строки в исходные бинарные данные платформа 1С предоставляет глобальную функцию Base64Значение(). На вход функция принимает исходную строку, а возвращает объект типа ДвоичныеДанные. Полученные двоичные данные можно сохранить в файл нужного расширения с помощью метода Записать().

Рассмотрим простейший пример кода:


// Входящая переменная СтрокаBase64 содержит тело файла ответа сервиса
ИмяФайла = "C:\Temp\Документ.pdf";

// Преобразуем строку Base64 в двоичные данные
ДвоичныеДанныеФайла = Base64Значение(СтрокаBase64);

// Записываем двоичные данные на диск
ДвоичныеДанныеФайла.Записать(ИмяФайла);

Этот код отлично работает в стандартных ситуациях, однако на практике интеграция часто требует учета ряда технических нюансов: нормализации данных, проверки формата и корректного разделения серверного и клиентского контекста.

Очистка и подготовка входящей строки Base64

Проанализируем типовые проблемы, из-за которых функция Base64Значение() может завершиться аварийно или сформировать поврежденный файл:

  1. Наличие Data URI заголовка. Современные веб-сервисы часто возвращают строку вместе с mime-префиксом, например: data:application/pdf;base64,JVBERi0x.... Этот заголовок не является частью полезных данных алгоритма Base64. Если передать его в Base64Значение(), платформа не сможет корректно распознать байты. Префикс обязательно нужно отсекать до запятой.
  2. Непечатаемые символы и переводы строк. В некоторых реализациях протоколов (например, SOAP или старых спецификациях MIME) длинные строки Base64 автоматически разбиваются символами переноса строк Символы.ПС и Символы.ВК. Их рекомендуется предварительно удалять.
  3. Безопасный для URL вариант (URL-Safe Base64). Если сторонний сервис кодирует строку с заменой спецсимволов (символ + заменяется на -, а / на _), стандартный декодер 1С вернет ошибку. В таком случае необходимо выполнить обратную замену символов и дополнить строку знаками = в конце, чтобы её общая длина была кратна 4.

Напишем универсальную функцию очистки строки перед её декодированием:


Функция ОчиститьСтрокуBase64(Знач ВходящаяСтрока)
    
    // 1. Удаляем префикс Data URI, если он присутствует
    ПозицияЗапятой = СтрНайти(ВходящаяСтрока, ",");
    Если ПозицияЗапятой > 0 И СтрНайти(НСтр(ВходящаяСтрока), "data:") > 0 Тогда
        ВходящаяСтрока = Сред(ВходящаяСтрока, ПозицияЗапятой + 1);
    КонецЕсли;
    
    // 2. Убираем переносы строк и пробелы
    ВходящаяСтрока = СтрЗаменить(ВходящаяСтрока, Символы.ПС, "");
    ВходящаяСтрока = СтрЗаменить(ВходящаяСтрока, Символы.ВК, "");
    ВходящаяСтрока = СтрЗаменить(ВходящаяСтрока, " ", "");
    
    // 3. Обрабатываем URL-safe Base64 при необходимости
    ВходящаяСтрока = СтрЗаменить(ВходящаяСтрока, "-", "+");
    ВходящаяСтрока = СтрЗаменить(ВходящаяСтрока, "_", "/");
    
    ОстатокОтДеления = СтрДлина(ВходящаяСтрока) % 4;
    Если ОстатокОтДеления > 0 Тогда
        ВходящаяСтрока = ВходящаяСтрока + СтрШаблон("%1", ?(ОстатокОтДеления = 2, "==", "="));
    КонецЕсли;
    
    Возврат ВходящаяСтрока;
    
КонецФункции

Проверка корректности полученного PDF-документа

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

Посмотрим на пример проверки сигнатуры через объект ЧтениеДанных:


Функция ЭтоКорректныйPDF(ДвоичныеДанныеФайла)
    
    Если ДвоичныеДанныеФайла = Неопределено Тогда
        Возврат Ложь;
    КонецЕсли;
    
    Поток = ДвоичныеДанныеФайла.ОткрытьПотокДляЧтения();
    Чтение = Новый ЧтениеДанных(Поток);
    
    // Считываем первые 5 байт
    Буфер = Чтение.ПрочитатьВБуферДвоичныхДанных(5);
    Чтение.Закрыть();
    
    СтрокаСигнатуры = ПолучитьСтрокуИзБуфераДвоичныхДанных(Буфер, КодировкаТекста.ANSI);
    
    // Проверяем соответствие заголовку %PDF-
    Возврат (СтрокаСигнатуры = "%PDF-");
    
КонецФункции

Организация работы в клиент-серверной архитектуре

В реальных проектах на управляемых формах вызов веб-сервиса и декодирование происходят на стороне сервера. Если вызвать ДвоичныеДанные.Записать() на сервере, файл будет сохранен в файловой системе сервера 1С:Предприятия, и клиентский компьютер пользователя не получит к нему доступа.

Разберем правильный алгоритм передачи файла:

  1. На сервере получаем строку Base64 из веб-сервиса, очищаем её и декодируем через Base64Значение().
  2. Помещаем полученный объект ДвоичныеДанные во временное хранилище с помощью метода ПоместитьВоВременноеХранилище().
  3. На клиенте получаем адрес во временном хранилище, извлекаем данные во временный файл клиентской машины через ПолучитьИмяВременногоФайла("pdf") и метод ПолучитьФайлИзВременногоХранилища().
  4. Открываем готовый файл внешней программой просмотра с помощью ЗапуститьПриложение().

Посмотрим на реализацию в коде формы:


&НаСервере
Функция ПолучитьФайлPDFНаСервере()
    
    // Имитация получения данных из сервиса
    СтрокаBase64 = ПолучитьОтветВебСервиса(); 
    СтрокаBase64 = ОчиститьСтрокуBase64(СтрокаBase64);
    
    ДвоичныеДанныеPDF = Base64Значение(СтрокаBase64);
    
    Если Не ЭтоКорректныйPDF(ДвоичныеДанныеPDF) Тогда
        ВызватьИсключение "Сервер вернул поврежденный или неверный формат PDF!";
    КонецЕсли;
    
    // Помещаем во временное хранилище и возвращаем адрес на клиент
    Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанныеPDF, УникальныйИдентификатор);
    
КонецФункции

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

Оптимизация потребления памяти при работе с большими файлами

Обратите внимание на важную особенность платформы 1С: строки в оперативной памяти хранятся в кодировке UTF-16, то есть каждый символ занимает 2 байта. Сам по себе алгоритм Base64 увеличивает объем исходного файла примерно на 33%. Таким образом, обработка PDF-файла размером в 50 Мб в виде строки Base64 может потребовать от 150 до 200 Мб оперативной памяти только на поддержание переменной в контексте.

Если веб-сервер позволяет отдавать данные без сериализации в JSON/XML, а напрямую в бинарном формате application/pdf, предпочтительнее выполнять HTTP-запрос через объект HTTPСоединение и сохранять ответ напрямую на диск методом HTTPОтвет.ПолучитьТелоКакИмяФайла(). Это полностью исключит загрузку тяжелых строк в оперативную память сервера.

← На главную