Как в 1С 8.3 передать файл и дополнительные параметры через POST-запрос в формате multipart/form-data?

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

В процессе интеграции 1С с внешними веб-сервисами часто возникает задача передачи не только текстовых данных (например, в формате JSON), но и бинарных файлов (PDF, изображения, архивы). В популярных инструментах отладки, таких как Postman, это реализуется простым выбором типа тела запроса form-data. Однако в платформе «1С:Предприятие 8.3» формирование такого запроса требует ручной сборки тела сообщения согласно спецификациям протокола HTTP.

Рассмотрим подробно, как правильно сконструировать запрос типа multipart/form-data, чтобы сервер мог корректно распознать и файл, и сопутствующие параметры. Мы проанализируем наиболее распространенные ошибки, такие как получение кода ответа 422, и разберем проверенные способы решения задачи.

Понятие multipart-запроса и роль Boundary

Когда мы отправляем данные через multipart/form-data, тело HTTP-запроса разделяется на несколько частей. Каждая часть содержит собственные заголовки и данные (текстовое поле или содержимое файла). Для разделения этих частей используется уникальная строка-разделитель, называемая boundary. Проанализируем структуру такого тела:

  1. Начало части: две дефиса -- плюс значение boundary.
  2. Заголовки части: например, Content-Disposition: form-data; name="имя_поля".
  3. Пустая строка (разделитель заголовка и данных).
  4. Само значение поля или бинарные данные файла.
  5. Завершение запроса: две дефиса --, значение boundary и еще два дефиса --.

Выясним причину частой ошибки 422 (Unprocessable Entity). Чаще всего она связана с тем, что структура multipart сформирована некорректно: либо не соблюдены правила расстановки пустых строк, либо используются неправильные символы перевода строки. Стандарт HTTP требует использования последовательности CRLF (Возврат каретки + Перевод строки), что в 1С соответствует Символы.ВК + Символы.ПС.

Способ 1: Формирование тела запроса путем объединения файлов

Этот метод считается классическим для 1С. Он заключается в том, что мы создаем несколько временных файлов: текстовый файл с «шапкой» запроса, бинарный файл (сам передаваемый документ) и текстовый файл с «подвалом» запроса. Затем эти файлы объединяются в один общий файл тела, который и отправляется на сервер. Разберем этот алгоритм по шагам.

Сначала подготовим переменные и определим границы запроса:


ИмяФайлаДляЗагрузки = "C:\qwe.pdf";
Сервер = "new.example.kz";
АдресРесурса = "/api/explorer/file";
Boundary = Строка(Новый УникальныйИдентификатор());
МассивФайловДляОбъединения = Новый Массив;

Теперь сформируем текстовую часть, содержащую JSON-параметры и метаданные файла. Важно использовать правильную кодировку (обычно КодировкаТекста.ANSI или UTF-8 в зависимости от требований API):


ИмяФайлаОтправкиНачало = ПолучитьИмяВременногоФайла("txt"); 
ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяФайлаОтправкиНачало, КодировкаТекста.ANSI, Символы.ВК + Символы.ПС); 

// Добавляем параметр "json"
ФайлОтправкиНачало.ЗаписатьСтроку("--" + Boundary);
ФайлОтправкиНачало.ЗаписатьСтроку("Content-Disposition: form-data; name=""json""");
ФайлОтправкиНачало.ЗаписатьСтроку(""); // Пустая строка перед значением
ФайлОтправкиНачало.ЗаписатьСтроку("{}");

// Добавляем параметр "type"
ФайлОтправкиНачало.ЗаписатьСтроку("--" + Boundary);
ФайлОтправкиНачало.ЗаписатьСтроку("Content-Disposition: form-data; name=""type""");
ФайлОтправкиНачало.ЗаписатьСтроку("");
ФайлОтправкиНачало.ЗаписатьСтроку("File1c");

// Оформляем заголовок перед бинарными данными файла
ФайлОтправкиНачало.ЗаписатьСтроку("--" + Boundary); 
ФайлОтправкиНачало.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""qwe.pdf"""); 
ФайлОтправкиНачало.ЗаписатьСтроку("Content-Type: application/pdf"); 
ФайлОтправкиНачало.ЗаписатьСтроку(""); 
ФайлОтправкиНачало.Закрыть(); 

МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиНачало);

Далее добавим сам файл и завершающую границу. Посмотрим на пример завершения формирования тела:


// Добавляем файл документа в массив
МассивФайловДляОбъединения.Добавить(ИмяФайлаДляЗагрузки); 

// Формируем финальный фрагмент
ИмяФайлаОтправкиКонец = ПолучитьИмяВременногоФайла("txt"); 
ФайлОтправкиКонец = Новый ЗаписьТекста(ИмяФайлаОтправкиКонец, КодировкаТекста.ANSI, Символы.ВК + Символы.ПС); 
ФайлОтправкиКонец.ЗаписатьСтроку(""); 
ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary + "--"); 
ФайлОтправкиКонец.Закрыть(); 

МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиКонец); 

// Объединяем все части в итоговый файл
ИмяФайлаТела = ПолучитьИмяВременногоФайла("tmp");
ОбъединитьФайлы(МассивФайловДляОбъединения, ИмяФайлаТела);

На завершающем этапе создаем HTTPСоединение и выполняем отправку. Обратите внимание на заголовок Content-Type, в котором обязательно должен быть указан boundary.


Заголовки = Новый Соответствие();
Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
Заголовки.Вставить("Authorization", "Bearer " + МойТокен);

Соединение = Новый HTTPСоединение(Сервер, 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
ЗапросHTTP = Новый HTTPЗапрос(АдресРесурса, Заголовки);
ЗапросHTTP.УстановитьИмяФайлаТела(ИмяФайлаТела);

Ответ = Соединение.ОтправитьДляОбработки(ЗапросHTTP);

Способ 2: Использование Буфера двоичных данных (для платформы 8.3.9+)

Работа с временными файлами на диске может замедлять работу системы при массовой загрузке документов. Рассмотрим более современный подход с использованием объекта БуферДвоичныхДанных. Это позволяет собрать тело запроса целиком в оперативной памяти.

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


ПотокТела = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(ПотокТела, КодировкаТекста.UTF8);

// Записываем текстовую часть
ЗаписьДанных.ЗаписатьСтроку("--" + Boundary);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""name""");
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("TestName");
ЗаписьДанных.ЗаписатьСтроку("--" + Boundary);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""document.pdf""");
ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/pdf");
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.СброситьБуфер(); // Очищаем буфер записи в поток

// Записываем файл из ДвоичныхДанных
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяФайлаДляЗагрузки);
ДвоичныеДанныеФайла.Записать(ПотокТела);

// Записываем финальный Boundary
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("--" + Boundary + "--");
ЗаписьДанных.Закрыть();

// Получаем тело для запроса
РезультатТело = ПотокТела.ЗакрытьИПолучитьДвоичныеДанные();
ЗапросHTTP.УстановитьТелоИзДвоичныхДанных(РезультатТело);

Важные технические нюансы

При реализации передачи файлов важно учитывать следующие моменты, которые помогут избежать ошибок при интеграции:

  1. Кодировка имен файлов: Если имя файла содержит кириллицу, сервер может его не принять. В таких случаях рекомендуется либо переименовывать файл в латиницу перед отправкой, либо использовать кодирование RFC 2047.
  2. Content-Length: Платформа 1С автоматически вычисляет размер тела при использовании методов УстановитьИмяФайлаТела или УстановитьТелоИзДвоичныхДанных. Не устанавливайте этот заголовок вручную, чтобы избежать расхождений в байтах, особенно если вы используете спецсимволы.
  3. Использование БСП: Если ваша конфигурация использует «Библиотеку стандартных подсистем», загляните в модуль РаботаСHTTP. Там реализованы вспомогательные функции для формирования multipart-запросов, которые делают код более лаконичным и безопасным.
  4. Тип содержимого (MIME-тип): Хотя часто советуют использовать application/x-octet-stream, лучше указывать точный тип (например, application/pdf, image/png). Некоторые строгие API проверяют соответствие расширения файла и его Content-Type в заголовке части.

Разберем еще один совет: для отладки структуры multipart используйте локальный прокси-сервер (например, Fiddler). Настройте HTTPСоединение на работу через прокси и сравните байт в байт запрос из Postman и запрос из 1С. Это позволит моментально увидеть лишние переносы строк или отсутствующие тире в boundary.

Таким образом, мы рассмотрели два надежных способа передачи файлов методом POST. Выбор между ними зависит от версии вашей платформы и требований к производительности. Использование БуфераДвоичныхДанных является более современным стандартом, в то время как ОбъединитьФайлы гарантированно работает на любых версиях 8.3.

← На главную