При интеграции 1С с внешними веб-сервисами программисты часто сталкиваются с необходимостью отправки данных в формате multipart/form-data. Это стандартный способ передачи файлов и текстовых полей в одном запросе. Однако при ручном формировании тела запроса часто возникают ошибки, из-за которых сервер не может корректно «распарсить» данные, а инструменты отладки (например, Fiddler) показывают «кашу» из заголовков и значений. Разберем детально, как решить эту задачу и какие нюансы платформы 1С следует учитывать.
Основная сложность заключается в том, что протокол HTTP жестко регламентирует использование символов перевода строки. Согласно стандартам RFC, каждая строка в заголовках и теле multipart-запроса должна заканчиваться последовательностью CRLF (возврат каретки + перевод строки). В 1С при использовании многострочных строк (через вертикальную черту |) в зависимости от операционной системы или настроек может использоваться только символ ПС (LF). Для многих современных веб-серверов (особенно на базе IIS или строгих конфигураций Nginx/Apache) одиночного символа ПС недостаточно, чтобы распознать окончание заголовка или части данных.
Рассмотрим решение, которое нашел автор темы. Вместо того чтобы полагаться на автоматический перенос строк платформой, необходимо явно добавлять системные символы Символы.ВК + Символы.ПС.
Разделитель = "----WebKitFormBoundary" + Строка(Новый УникальныйИдентификатор());
ПереносСтроки = Символы.ВК + Символы.ПС;
ТелоЗапроса = "--" + Разделитель + ПереносСтроки;
ТелоЗапроса = ТелоЗапроса + "Content-Disposition: form-data; name=""file""; filename=""q.pdf""" + ПереносСтроки;
ТелоЗапроса = ТелоЗапроса + "Content-Type: application/pdf" + ПереносСтроки + ПереносСтроки;
ТелоЗапроса = ТелоЗапроса + "1" + ПереносСтроки; // Здесь должны быть данные файла
ТелоЗапроса = ТелоЗапроса + "--" + Разделитель + "--" + ПереносСтроки;
Обратим внимание на пустую строку (двойной ПереносСтроки) после заголовка Content-Type. Это критически важный момент: пустая строка отделяет метаданные части запроса от ее фактического содержимого.
В примере выше автор пытался передать содержимое PDF-файла просто как текст («1»). Это допустимо только для тестирования. На практике использование метода УстановитьТелоИзСтроки для передачи бинарных файлов (PDF, PNG, EXE) приведет к повреждению файла, так как 1С попытается интерпретировать байты файла как символы определенной кодировки.
Для правильной сборки запроса с реальным файлом проанализируем использование объекта ЗаписьДанных. Этот метод позволяет последовательно записывать в один поток и текстовые части разделителей, и «сырые» байты файла.
// Создаем поток в памяти для сборки тела
ПотокТела = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(ПотокТела, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Разделитель = "----MyBoundary";
CRLF = Символы.ВК + Символы.ПС;
// 1. Записываем текстовую часть (начало)
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + CRLF);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""document.pdf""" + CRLF);
ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/pdf" + CRLF + CRLF);
ЗаписьДанных.СброситьБуфер(); // Выталкиваем текст в поток
// 2. Записываем содержимое файла напрямую из ДвоичныеДанные
ФайлДД = Новый ДвоичныеДанные("C:\temp\document.pdf");
ПотокТела.КопироватьИз(ФайлДД.ОткрытьПотокДляЧтения());
// 3. Записываем финальный разделитель
ЗаписьДанных.ЗаписатьСтроку(CRLF + "--" + Разделитель + "--" + CRLF);
ЗаписьДанных.Закрыть();
// Получаем итоговое тело для запроса
РезультатТело = ПотокТела.ЗакрытьИПолучитьДвоичныеДанные();
Запрос.УстановитьТелоИзДвоичныхДанных(РезультатТело);
Важно: При использовании кодировки UTF-8 обязательно указывайте параметр ИспользованиеByteOrderMark.НеИспользовать. Если 1С добавит BOM-символы в начало текстового блока, сервер может не опознать разделитель (boundary), так как он перестанет соответствовать значению, указанному в заголовке Content-Type.
Как отметили участники обсуждения, ручная сборка multipart-запросов — процесс трудоемкий и чреват ошибками (например, легко забыть два дефиса в конце последнего разделителя). Профессиональное сообщество рекомендует использовать готовые инструменты, такие как библиотека «Коннектор» (разработка Василия Бондаревского).
Преимущества использования библиотеки:
application/pdf.Однако, как справедливо заметили в комментариях, при использовании сторонних библиотек важно понимать их внутреннее устройство. Некоторые реализации могут не учитывать все многообразие кодов состояния HTTP (Response Status Codes), поэтому всегда полезно иметь возможность заглянуть в исходный код и адаптировать его под свои нужды.
Если сервер продолжает выдавать ошибку (например, 400 Bad Request), выполним следующие шаги:
Content-Type в объекте HTTPЗапрос. Он должен выглядеть так: multipart/form-data; boundary=ваш_разделитель.--.--ваш_разделитель--.0D 0A (CRLF).Content-Length. Если вы формируете тело через ПотокВПамяти, 1С обычно вычисляет размер автоматически, но при ручных манипуляциях со строками и двоичными данными размер может «поплыть» из-за разницы в кодировках.Выясним причину некорректного парсинга в Fiddler, упомянутую автором: когда Name и Value смешиваются, это почти всегда означает отсутствие пустой строки после блока Content-Disposition. Сервер просто считает, что ваши данные — это продолжение заголовков. Явное использование Символы.ВК + Символы.ПС гарантированно решает эту проблему.