Как передать несколько файлов в теле одного HTTP-запроса в 1С?

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

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

Способ 1: Использование составных сообщений (Multipart/form-data)

Это стандартный и наиболее правильный с точки зрения протокола HTTP способ. Начиная с версии платформы 1С 8.3.18, разработчикам стали доступны специализированные объекты ЗаписьСоставногоСообщения и ЧтениеСоставногоСообщения, которые значительно упрощают формирование запросов в формате multipart/form-data. Рассмотрим по шагам, как реализовать этот подход.

Преимущества: соответствие мировым стандартам, отсутствие необходимости кодировать бинарные данные в текст (что экономит трафик), возможность задавать метаданные для каждой части сообщения.

Проанализируем пример кода для отправки двух файлов:


// Создаем уникальный разделитель (boundary)
Разделитель = Строка(Новый УникальныйИдентификатор);

// Создаем объект для записи составного сообщения
ЗаписьСоставного = Новый ЗаписьСоставногоСообщения;
ЗаписьСоставного.УстановитьРазделитель(Разделитель);

// Добавляем первый файл
ЗаголовкиФайла1 = Новый Соответствие;
ЗаголовкиФайла1.Вставить("Content-Disposition", "form-data; name=""file1""; filename=""report.pdf""");
ЗаголовкиФайла1.Вставить("Content-Type", "application/pdf");

Часть1 = Новый ПотокВПамяти; // Или чтение из файла
ДвоичныеДанныеФайла1 = Новый ДвоичныеДанные("C:\temp\report.pdf");
ДвоичныеДанныеФайла1.Записать(Часть1);
Часть1.ПерейтиВНачало();

ЗаписьСоставного.ДобавитьЧасть(Часть1, ЗаголовкиФайла1);

// Добавляем второй файл
ЗаголовкиФайла2 = Новый Соответствие;
ЗаголовкиФайла2.Вставить("Content-Disposition", "form-data; name=""file2""; filename=""image.png""");
ЗаголовкиФайла2.Вставить("Content-Type", "image/png");

ДвоичныеДанныеФайла2 = Новый ДвоичныеДанные("C:\temp\image.png");
Часть2 = Новый ПотокВПамяти;
ДвоичныеДанныеФайла2.Записать(Часть2);
Часть2.ПерейтиВНачало();

ЗаписьСоставного.ДобавитьЧасть(Часть2, ЗаголовкиФайла2);

// Формируем итоговое тело запроса
ПотокТела = ЗаписьСоставного.ПолучитьПоток();

// Настраиваем HTTP-запрос
Запрос = Новый HTTPЗапрос("/upload");
Запрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Разделитель);
Запрос.УстановитьТелоИзПотока(ПотокТела);

Выясним причину использования УстановитьТелоИзПотока: этот метод позволяет передавать данные напрямую, не загружая их целиком в оперативную память в виде одного огромного объекта ДвоичныеДанные. Это критически важно, если вы передаете файлы размером в сотни мегабайт.

Способ 2: Сериализация в JSON (Base64)

Если принимающая сторона — это другой веб-сервис, работающий исключительно с JSON, мы можем упаковать файлы в структуру JSON. Однако важно помнить, что бинарные данные нельзя просто вставить в текст — их необходимо закодировать в Base64.

Важный нюанс: кодирование в Base64 увеличивает размер передаваемых данных ровно на 33%. Если ваш файл весил 100 МБ, в JSON-запросе он превратится в 133 МБ. Рассмотрим, как это реализовать:

  1. Создаем массив или структуру для хранения данных.
  2. Для каждого файла получаем ДвоичныеДанные.
  3. Используем функцию Base64Строка() для преобразования данных в текстовый вид.
  4. Сериализуем итоговую структуру в строку JSON.

Посмотрим на пример кода:


ДанныеДляОтправки = Новый Структура;
МассивФайлов = Новый Массив;

// Обрабатываем первый файл
Файл1 = Новый ДвоичныеДанные("C:\docs\contract.docx");
Инфо1 = Новый Структура;
Инфо1.Вставить("ИмяФайла", "contract.docx");
Инфо1.Вставить("Данные", Base64Строка(Файл1));
МассивФайлов.Добавить(Инфо1);

// Обрабатываем второй файл
Файл2 = Новый ДвоичныеДанные("C:\docs\invoice.pdf");
Инфо2 = Новый Структура;
Инфо2.Вставить("ИмяФайла", "invoice.pdf");
Инфо2.Вставить("Данные", Base64Строка(Файл2));
МассивФайлов.Добавить(Инфо2);

ДанныеДляОтправки.Вставить("Files", МассивФайлов);

// Сериализация
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ДанныеДляОтправки);
СтрокаЗапроса = ЗаписьJSON.Закрыть();

HTTPЗапрос = Новый HTTPЗапрос("/api/v1/save-files");
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса);

Способ 3: Предварительная архивация в ZIP

Если требования к формату на принимающей стороне позволяют, самым простым и эффективным способом передачи множества файлов будет их предварительная упаковка в один архив. Это решает проблему "один запрос — одно тело" максимально эффективно.

Разберем алгоритм действий (для работы с архивами также может пригодиться специализированный распаковщик и запаковщик файлов):

  1. Создаем объект ЗаписьZipФайла.
  2. Добавляем в него все необходимые файлы с помощью метода Добавить().
  3. Сохраняем архив во временный файл.
  4. Передаем этот архив через УстановитьТелоИзДвоичныхДанных.

Важное замечание о сжатии: как справедливо отмечено в обсуждении, двоичные данные (особенно уже сжатые форматы вроде JPEG или PDF) практически не уменьшаются в объеме при архивации. Смысл архива здесь не в экономии места, а в удобстве передачи всей пачки файлов одним "куском".

Способ 4: Оптимизация передачи больших файлов через потоки

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

Если вы работаете на платформе ниже 8.3.18 и вам нужно реализовать Multipart вручную, вам придется самостоятельно записывать в поток границы (boundaries) в формате --BoundaryName, заголовок Content-Disposition и саму последовательность байтов файла. Это трудоемкий процесс, требующий внимательности к переносам строк (используйте Символы.ПС или Символы.ВК + Символы.ПС в зависимости от требований сервера).

Параллельная отправка или один большой файл?

В ходе дискуссии был поднят вопрос производительности: что быстрее — отправить один большой файл или много маленьких? Практика показывает следующее:

Итоги и рекомендации

Подводя итог, выберем оптимальный путь решения задачи:

  1. Если версия платформы позволяет (8.3.18+), используйте ЗаписьСоставногоСообщения. Это самый надежный и современный метод.
  2. Если нужно передать данные "просто и быстро" и объем невелик — используйте JSON + Base64 (это часто применяется при получении токена авторизации RuStore).
  3. Если файлов сотни — упаковывайте их в ZIP и отправляйте один файл.
  4. Всегда используйте УстановитьТелоИзПотока вместо УстановитьТелоИзДвоичныхДанных для работы с файлами, размер которых превышает 10-20 МБ, чтобы избежать проблем с производительностью сервера 1С.

Помните, что при ручном формировании multipart-запросов (без встроенных объектов 1С) крайне важно следить за корректностью кодировки текстовых частей и отсутствием лишних пробелов в строках разделителей.

← На главную