При интеграции систем часто возникает задача передачи файлов через веб-сервисы. Если с передачей одного файла все относительно просто (для этого используется стандартный тип base64Binary), то передача целого массива двоичных данных в рамках одного запроса в SOAP-сервисах 1С может вызвать затруднения. Особенно это актуально для устаревших версий платформы (например, 1С 8.3.4), где еще нет полноценной поддержки легковесных HTTP-сервисов, и разработчикам приходится работать исключительно в рамках протокола SOAP.
В этой статье мы подробно разберем несколько эффективных способов решения этой задачи, начиная от простых обходных путей с применением JSON и заканчивая профессиональными архитектурными паттернами для обмена большими объемами данных.
Давайте рассмотрим один из самых популярных и простых в реализации подходов, который не требует глубокой настройки внутренней структуры веб-сервиса и создания кастомных XDTO-пакетов. Суть метода заключается в том, что все файлы на стороне клиента кодируются в формат Base64, упаковываются в массив, преобразуются в JSON-строку и передаются в веб-сервис в качестве одного строкового параметра.
Разберем этот процесс по шагам со стороны клиента и сервера:
base64_encode(), формируем стандартный массив строк и сериализуем его в JSON-строку с помощью функции json_encode().string (из пространства имен http://www.w3.org/2001/XMLSchema).Давайте проанализируем пример кода на стороне 1С, который выполняет десериализацию полученной JSON-строки и восстанавливает файлы:
// Функция веб-сервиса, принимающая JSON-строку с массивом файлов
Функция ПринятьМассивФайловВФорматеJSON(ДанныеФайловJSON) Экспорт
// Инициализируем чтение JSON
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(ДанныеФайловJSON);
Попытка
МассивСтрокBase64 = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Исключение
ЧтениеJSON.Закрыть();
ВызватьИсключение "Не удалось разобрать входящий JSON-пакет: " + ОписаниеОшибки();
КонецПопытки;
Если ТипЗнч(МассивСтрокBase64) <> Тип("Массив") Тогда
ВызватьИсключение "Переданные данные не являются массивом!";
КонецЕсли;
// Обходим массив и восстанавливаем двоичные данные
Для Каждого СтрокаBase64 Из МассивСтрокBase64 Цикл
Попытка
// Преобразуем Base64 обратно в ДвоичныеДанные
ДвоичныеДанныеФайла = ПолучитьДвоичныеДанныеИзСтрокиBase64(СтрокаBase64);
// Здесь выполняем сохранение двоичных данных в базу (например, в ХранилищеЗначения)
// или записываем на диск во временный файл
ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
Исключение
// Логируем ошибку обработки конкретного файла
ЗаписьЖурналаРегистрации("ВебСервис.ЗагрузкаФайлов",
УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка обработки файла: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Возврат "Успешно обработано файлов: " + МассивСтрокBase64.Количество();
КонецФункции
Преимущества метода: Простота реализации, отсутствие необходимости настраивать сложные типы в конфигурации 1С, легкая интеграция с любыми внешними языками программирования (PHP, Python, JS), которые имеют встроенную поддержку JSON.
Если мы хотим оставаться в рамках строгой типизации SOAP и XML-стандартов, то правильным решением будет создание собственного XDTO-пакета. Это позволит веб-сервису автоматически валидировать структуру входящего XML-запроса.
Выясним, как правильно настроить свойства объектов XDTO в конфигураторе 1С:
http://www.myservice.ru/files.FileTransferElement. Добавьте в него свойство FileData с типом base64Binary (из пространства http://www.w3.org/2001/XMLSchema) и свойство FileName с типом string.ArrayOfFiles. Добавьте в него свойство File.File укажите тип FileTransferElement, а свойствам МинимальноеКоличество и МаксимальноеКоличество задайте значения 0 и -1 (Unbounded) соответственно. Это укажет платформе 1С, что данное свойство является списком (массивом).ArrayOfFiles из вашего пространства имен XDTO.Рассмотрим, как обрабатывать такие строго типизированные данные в коде веб-сервиса 1С:
Функция ЗагрузитьФайлыЧерезXDTO(СписокФайловXDTO) Экспорт
// На стороне 1С параметр СписокФайловXDTO представит собой ОбъектXDTO
// Доступ к списку элементов осуществляется через свойство "File"
СписокЭлементов = СписокФайловXDTO.File;
Для Каждого ЭлементXDTO Из СписокЭлементов Цикл
// Получаем имя файла и двоичные данные
ИмяФайла = ЭлементXDTO.FileName;
// Платформа автоматически преобразует base64Binary во временный файл или ДвоичныеДанные
ДвоичныеДанные = ЭлементXDTO.FileData;
// Пример сохранения в базу данных
НовыйДокумент = Справочники.ХранилищеФайлов.СоздатьЭлемент();
НовыйДокумент.Наименование = ИмяФайла;
НовыйДокумент.ДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные);
НовыйДокумент.Записать();
КонецЦикла;
Возврат "Файлы успешно импортированы";
КонецФункции
Если вам не хочется создавать кастомные XDTO-пакеты в конфигурации, но при этом хочется передавать массив данных стандартными средствами SOAP-сериализации XML, можно использовать встроенный универсальный тип anyType.
Посмотрим на особенности этого метода:
anyType (из пространства имен http://www.w3.org/2001/XMLSchema).СписокXDTO.Проанализируем пример обработки параметра с типом anyType:
Функция ОбработатьУниверсальныйМассив(ДанныеМассива) Экспорт
Если ТипЗнч(ДанныеМассива) = Тип("СписокXDTO") Тогда
Для Каждого ЭлементСписка Из ДанныеМассива Цикл
// Извлекаем значение из XDTO-объекта
СтрокаBase64 = ЭлементСписка.Значение;
Попытка
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтрокиBase64(СтрокаBase64);
// Обработка двоичных данных...
Исключение
Продолжить;
КонецПопытки;
КонецЦикла;
КонецЕсли;
Возврат "Обработка завершена";
КонецФункции
Платформа 1С содержит встроенный системный пакет XDTO, описывающий базовые типы данных платформы. Мы можем использовать его для передачи стандартного массива:
http://v8.1c.ru/8.1/data/core.Array из этого пакета.
// Преобразуем XDTO-структуру "Array" напрямую в привычный Массив 1С
МассивФайлов = СериализаторXDTO.ПрочитатьXDTO(ПараметрМассиваXDTO);
Для Каждого СтрокаBase64 Из МассивФайлов Цикл
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтрокиBase64(СтрокаBase64);
// Выполняем запись
КонецЦикла;
Передача больших массивов файлов за один SOAP-запрос содержит в себе скрытые инфраструктурные риски, о которых важно помнить разработчику:
413 Payload Too Large, и управление даже не дойдет до кода 1С.Для создания надежных и отказоустойчивых систем обмена используется паттерн поблочной передачи (Chunking). Давайте разберем по шагам, как построить такую архитектуру:
PutFilePart(FileID, PartNumber, PartData, TotalParts).Рассмотрим пример реализации сборки поблочно передаваемого файла на стороне 1С:
Функция PutFilePart(FileID, PartNumber, PartData, TotalParts) Экспорт
// Определяем путь к временному файлу на сервере 1С
ИмяВременногоФайла = КаталогВременныхФайлов() + FileID + ".tmp";
// Преобразуем текущую часть из Base64 в ДвоичныеДанные
ДвоичныеДанныеЧасти = ПолучитьДвоичныеДанныеИзСтрокиBase64(PartData);
// Записываем полученную часть в конец собираемого файла
// Параметр "Истина" указывает на дозапись (добавление в конец файла)
ЗаписьДанных = Новый ЗаписьДанных(ИмяВременногоФайла, , , Истина);
ЗаписьДанных.Записать(ДвоичныеДанныеЧасти);
ЗаписьДанных.Закрыть();
// Если получена последняя часть, выполняем обработку файла
Если Число(PartNumber) = Число(TotalParts) Тогда
Попытка
// Временный файл полностью собран. Так как это ZIP-архив, распаковываем его
Архив = Новый ЧтениеZipФайла(ИмяВременногоФайла);
ПутьРаспаковки = КаталогВременныхФайлов() + FileID;
Архив.ИзвлечьВсе(ПутьРаспаковки, РежимВосстановленияПутейФайловZip.НеВосстанавливать);
Архив.Закрыть();
// Читаем извлеченные файлы и записываем их в БД
НайденныеФайлы = НайтиФайлы(ПутьРаспаковки, "*.*");
Для Каждого Файл Из НайденныеФайлы Цикл
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(Файл.ПолноеИмя);
// Сохранение в базу данных 1С...
// Удаляем временный распакованный файл
УдалитьФайлы(Файл.ПолноеИмя);
КонецЦикла;
// Удаляем сам архив и папку распаковки
УдалитьФайлы(ИмяВременногоФайла);
УдалитьФайлы(ПутьРаспаковки);
Исключение
УдалитьФайлы(ИмяВременногоФайла);
ВызватьИсключение "Ошибка сборки и обработки архива: " + ОписаниеОшибки();
КонецПопытки;
Возврат "Файл успешно собран и обработан";
КонецЕсли;
Возврат "Часть " + PartNumber + " из " + TotalParts + " успешно получена";
КонецФункции
Использование поблочной передачи гарантирует стабильную работу интеграции при любом качестве интернет-соединения и позволяет передавать гигабайты информации без перерасхода оперативной памяти сервера приложений 1С.