При интеграции 1С со сторонними Web-сервисами (SOAP) часто возникает задача передачи двоичных файлов: прайс-листов, документов, изображений или отчетов. Нередко файл упаковывается в архив и передается в виде текстовой строки в формате Base64 внутри объекта ОбъектXDTO. Однако попытка преобразовать полученную строку через стандартную функцию Base64Значение() может вернуть пустой результат или вызвать ошибку. Разберем по шагам, почему это происходит и как правильно получить, декодировать и сохранить файл.
Проанализируем структуру полученной строки. Встроенная функция платформы Base64Значение() очень чувствительна к формату входных данных. Внешние сервисы при сериализации Base64 могут добавлять переносы строк для ограничения длины, пробелы или использовать нестандартное кодирование.
Рассмотрим основные варианты некорректных данных и способы их нормализации:
\r\n, \n или пробелы, их необходимо удалить перед передачей в функцию декодирования.+ на - и / на _, а также убирают символы дополнения = в конце строки. Платформа 1С не сможет декодировать такую строку без обратной замены.data:application/zip;base64,, этот префикс необходимо отсечь до запятой.Посмотрим на пример универсальной очистки строки перед декодированием:
// Очищаем строку от пробелов и переносов строк
СтрокаBase64 = СтрЗаменить(СтрЗаменить(СтрЗаменить(ИсходнаяСтрока, Символы.ПС, ""), Символы.ВК, ""), " ", "");
// Проверяем наличие Data URI и отсекаем его
ПозицияЗапятой = СтрНайти(СтрокаBase64, ",");
Если ПозицияЗапятой > 0 И СтрНайти(Лев(СтрокаBase64, ПозицияЗапятой), "base64") > 0 Тогда
СтрокаBase64 = Сред(СтрокаBase64, ПозицияЗапятой + 1);
КонецЕсли;
// Преобразуем URL-safe Base64 в стандартный Base64
СтрокаBase64 = СтрЗаменить(СтрЗаменить(СтрокаBase64, "-", "+"), "_", "/");
// Дополняем выравнивание до кратности 4
Остаток = СтрДлина(СтрокаBase64) % 4;
Если Остаток > 0 Тогда
СтрокаBase64 = СтрокаBase64 + СтрШаблон("%1", ?(Остаток = 2, "==", "="));
КонецЕсли;
// Получаем двоичные данные
ДвоичныеДанныеФайла = Base64Значение(СтрокаBase64);
Выясним причину на уровне архитектуры SOAP-сервиса. По умолчанию, если свойство в WSDL или XSD схеме пакета XDTO описано как xs:string, платформа 1С читает его как обычную текстовую строку. В этом случае программисту приходится вручную вызывать Base64Значение().
Если же в схеме пакета XDTO для свойства указать тип base64Binary (пространство имен http://www.w3.org/2001/XMLSchema), платформа выполнит автоматическое преобразование при десериализации ответа. Свойство полученного объекта ОбъектXDTO сразу будет содержать тип ДвоичныеДанные, и необходимость в ручном декодировании полностью отпадет.
Посмотрим на пример обращения к свойству при правильной типизации XDTO:
// Если свойство типизировано как xs:base64Binary
ДвоичныеДанныеФайла = Ответ.pricelist_encoded_file;
// Проверяем тип полученного значения
Если ТипЗнч(ДвоичныеДанныеФайла) = Тип("ДвоичныеДанные") Тогда
ПутьКФайлу = ПолучитьИмяВременногоФайла("zip");
ДвоичныеДанныеФайла.Записать(ПутьКФайлу);
КонецЕсли;
Разберем ситуацию, когда во флаге ответа сервиса указано, что файл передается в сжатом виде (например, price_list_zipped = 1). После получения двоичных данных их необходимо сохранить во временный файл и распаковать с помощью объекта ЧтениеZipФайла.
Рассмотрим полный алгоритм распаковки и чтения файлов из архива:
ДвоичныеДанные во временный ZIP-файл с помощью метода ПолучитьИмяВременногоФайла("zip").ЧтениеZipФайла и извлекаем все файлы.Посмотрим на пример кода распаковки архива:
ВременныйZip = ПолучитьИмяВременногоФайла("zip");
КаталогРаспаковки = КаталогВременныхФайлов() + "Прайс_" + Строка(Новый УникальныйИдентификатор) + "\";
// Сохраняем двоичные данные на диск
ДвоичныеДанныеФайла.Записать(ВременныйZip);
// Распаковываем архив
Архив = Новый ЧтениеZipФайла(ВременныйZip);
Архив.ИзвлечьВсе(КаталогРаспаковки, РежимВосстановленияПутейФайловZIP.НеВосстанавливать);
Архив.Закрыть();
// Обрабатываем извлеченные файлы
НайденныеФайлы = НайтиФайлы(КаталогРаспаковки, "*.*");
Для Каждого Файл Из НайденныеФайлы Цикл
// Выполняем чтение и обработку файла прайс-листа
Сообщить("Извлечен файл: " + Файл.Имя);
КонецЦикла;
// Очищаем временные файлы
УдалитьФайлы(ВременныйZip);
УдалитьФайлы(КаталогРаспаковки);
Таким образом, при работе с Base64 через Web-сервисы всегда проверяйте корректность описания типов в пакетах XDTO и очищайте входную строку от лишних символов перед декодированием.