Передача файлов по протоколу HTTP с использованием формата multipart/form-data — одна из самых распространенных задач при интеграции 1С с внешними веб-сервисами, API сайтов или облачными хранилищами. Однако именно здесь программисты часто сталкиваются с трудноуловимыми ошибками: сервер возвращает «Bad Request», файлы приходят поврежденными («битыми»), а картинки не открываются. В этой статье мы подробно разберем, как правильно сформировать тело запроса, избежать проблем с кодировками и какой метод платформы 1С является наиболее современным и надежным.
Прежде чем переходить к коду, проанализируем, как должен выглядеть корректный запрос. Формат multipart/form-data подразумевает, что тело сообщения разделено на части специальной строкой-разделителем (boundary). Каждая часть содержит собственные заголовки (например, Content-Disposition) и сами данные.
Важно помнить, что согласно спецификации RFC, каждая строка в теле запроса должна заканчиваться строго парой символов CRLF (возврат каретки и перевод строки). В 1С это соответствует комбинации Символы.ВК + Символы.ПС.
Рассмотрим ситуацию, когда разработчик пытается собрать тело запроса, используя объект ТекстовыйДокумент. На первый взгляд это удобно, так как метод ДобавитьСтроку автоматически вставляет разделители. Однако здесь кроется главная причина повреждения файлов.
Проанализируем причину: когда мы добавляем ДвоичныеДанные картинки в текстовый объект, 1С пытается интерпретировать байты файла как символы текущей кодировки (например, UTF-8). Бинарный код изображения содержит последовательности байтов, которые не являются валидными символами. Платформа заменяет их на знаки вопроса или другие «заполнители», что безвозвратно портит файл. В итоге на сервер приходит текст, который невозможно превратить обратно в исходную картинку.
Вывод: никогда не используйте текстовые переменные или ТекстовыйДокумент для хранения и передачи содержимого файлов. Для этих целей подходят только ПотокВПамяти или специализированные объекты платформы.
Правильный подход при «ручной» сборке тела запроса — использование объекта ПотокВПамяти. Это позволяет записывать текстовые заголовки и двоичные данные файла в один поток байтов без искажений. Рассмотрим пример реализации:
Разделитель = "----" + Строка(Новый УникальныйИдентификатор());
ТелоЗапроса = Новый ПотокВПамяти;
// Используем кодировку UTF-8 без BOM для текстовых частей
ЗаписьДанных = Новый ЗаписьДанных(ТелоЗапроса, КодировкаТекста.UTF8, , Символы.ВК + Символы.ПС, , ИспользованиеByteOrderMark.НеИспользовать);
// 1. Записываем текстовое поле
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""comment""");
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("Вот ваш файл");
// 2. Записываем заголовок файла
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""image.jpg""");
ЗаписьДанных.ЗаписатьСтроку("Content-Type: image/jpeg");
ЗаписьДанных.ЗаписатьСтроку("");
// Сначала сбрасываем буфер текстовой записи
ЗаписьДанных.СброситьБуфер();
// 3. Записываем сами двоичные данные файла напрямую в поток
ДвоичныеДанныеФайла = Новый ДвоичныеДанные("C:\temp\image.jpg");
ТелоЗапроса.Записать(ДвоичныеДанныеФайла);
// 4. Завершаем формирование тела
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("--" + Разделитель + "--");
ЗаписьДанных.Закрыть();
ДанныеДляОтправки = ТелоЗапроса.ЗакрытьИПолучитьДвоичныеДанные();
Одной из скрытых причин ошибки «400 Bad Request» является наличие метки BOM в начале тела запроса. Byte Order Mark — это спецсимволы (EF BB BF для UTF-8), которые 1С может добавить в поток при инициализации ЗаписиДанных. Многие веб-серверы не ожидают увидеть эти байты перед разделителем boundary и считают запрос некорректным.
Чтобы избежать этой проблемы, всегда явно указывайте параметр ИспользованиеByteOrderMark.НеИспользовать при создании объекта ЗаписьДанных. Это гарантирует, что в поток попадут только те данные, которые вы записали сами.
Выясним еще одну причину неудач, с которой часто сталкиваются при копировании примеров кода из интернета. Это использование кириллических символов в названиях заголовков. Например, латинская «C» и русская «С» выглядят идентично, но имеют разные коды. Заголовок Сontent-Disposition, написанный с русской «С», не будет распознан сервером.
Для диагностики таких ошибок рекомендуем использовать инструменты отладки, такие как Fiddler или встроенный в 1С HexView (просмотр данных в шестнадцатеричном виде). Если вы видите в Hex-коде последовательность D0 AC вместо 43 (латинская C), значит, вкралась кириллица.
Начиная с версии платформы 8.3.10, в 1С появился объектный метод формирования multipart запросов, который избавляет разработчика от ручной манипуляции с потоками, разделителями и CRLF. Мы настоятельно рекомендуем использовать именно его.
Рассмотрим, как это упрощает код:
// Создаем массив для частей тела
ЧастиТела = Новый МассивЧастейТелаДляHTTPЗапроса;
// Создаем текстовую часть
ЧастьТекст = Новый ДанныеЧастиТелаДляHTTPЗапроса;
ЧастьТекст.Заголовки.Вставить("Content-Disposition", "form-data; name=""params""");
ЧастьТекст.УстановитьТелоИзСтроки("{""id"":123}", КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
ЧастиТела.Добавить(ЧастьТекст);
// Создаем часть с файлом
ЧастьФайл = Новый ДанныеЧастиТелаДляHTTPЗапроса;
ЧастьФайл.Заголовки.Вставить("Content-Disposition", "form-data; name=""file""; filename=""photo.jpg""");
ЧастьФайл.Заголовки.Вставить("Content-Type", "image/jpeg");
ЧастьФайл.УстановитьТелоИзДвоичныхДанных(Новый ДвоичныеДанные(ПутьКФайлу));
ЧастиТела.Добавить(ЧастьФайл);
// Формируем запрос
Разделитель = Строка(Новый УникальныйИдентификатор());
HTTPЗапрос = Новый HTTPЗапрос("/upload");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Разделитель);
HTTPЗапрос.УстановитьТелоИзМассиваЧастейТела(ЧастиТела, Разделитель);
// Отправка
Ответ = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
Преимущества этого метода:
boundary и переводы строк CRLF.Если запрос все равно не проходит, проанализируем ситуацию с помощью внешних утилит:
Content-Length. Если длина отличается на 2-3 байта при идентичном содержимом, ищите лишние символы перевода строки или BOM.Используя объектную модель МассивЧастейТелаДляHTTPЗапроса и контролируя отсутствие лишних байтов в потоках, вы обеспечите стабильную передачу файлов любого типа из 1С в любую внешнюю систему.