Интеграция 1С с веб-сайтами и сторонними сервисами часто требует передачи двоичных данных, в частности — изображений. В протоколе HTTP для передачи файлов стандартно используется формат multipart/form-data. В данной статье мы подробно разберем, как правильно сформировать тело такого запроса, какие ошибки часто допускают разработчики и как сделать код более современным и надежным.
Когда мы отправляем форму с файлом через браузер, данные упаковываются в специальный формат, где разные части (поля текста и файлы) отделяются друг от друга уникальной строкой-разделителем, называемой Boundary. Наша задача в 1С — вручную или с помощью встроенных объектов собрать такое «тело» запроса.
Рассмотрим классический подход, основанный на объединении текстовых и двоичных файлов. Этот метод часто встречается в старых обработках, но он до сих пор работоспособен и наглядно показывает структуру пакета.
Типичный алгоритм формирования запроса выглядит следующим образом:
Boundary.Проанализируем пример кода, который формирует структуру запроса. Обратите внимание на формирование заголовков внутри тела.
// 1. Инициализация параметров
ИмяФайлаДляЗагрузки = "C:\Upload\picture.jpg";
Boundary = СтрЗаменить(Строка(Новый УникальныйИдентификатор()), "-", "");
ИмяФайлаОтправки = КаталогВременныхФайлов() + "post_body.txt";
// 2. Формируем начало запроса
ИмяФайлаНачало = ПолучитьИмяВременногоФайла("txt");
ФайлНачало = Новый ЗаписьТекста(ИмяФайлаНачало, КодировкаТекста.ANSI);
ФайлНачало.ЗаписатьСтроку("--" + Boundary);
ФайлНачало.ЗаписатьСтроку("Content-Disposition: form-data; name=""file1""; filename=""image.jpg""");
// ВАЖНО: Правильный MIME-тип для картинок
ФайлНачало.ЗаписатьСтроку("Content-Type: image/jpeg");
ФайлНачало.ЗаписатьСтроку(""); // Пустая строка перед двоичными данными
ФайлНачало.Закрыть();
// 3. Формируем конец запроса
ИмяФайлаКонец = ПолучитьИмяВременногоФайла("txt");
ФайлКонец = Новый ЗаписьТекста(ИмяФайлаКонец, КодировкаТекста.ANSI);
ФайлКонец.ЗаписатьСтроку("");
ФайлКонец.ЗаписатьСтроку("--" + Boundary + "--");
ФайлКонец.Закрыть();
// 4. Объединяем части
МассивФайлов = Новый Массив;
МассивФайлов.Добавить(ИмяФайлаНачало);
МассивФайлов.Добавить(ИмяФайлаДляЗагрузки);
МассивФайлов.Добавить(ИмяФайлаКонец);
ОбъединитьФайлы(МассивФайлов, ИмяФайлаОтправки);
В этом примере критически важным моментом является указание Content-Type. Если сервер ожидает изображение, а вы передаете application/x-zip-compressed или application/octet-stream, сервер может отклонить запрос с ошибкой «Неверный тип файла».
Разберем тонкости, которые часто остаются за кадром, но напрямую влияют на успешность загрузки.
1. Ошибка в окончаниях строк (CRLF)
Согласно спецификации HTTP, строки в multipart должны разделяться парой символов ВК + ПС (CR+LF). В 1С метод ЗаписатьСтроку() по умолчанию может использовать только ПС. Если сервер строгий (например, на базе Nginx), он не увидит границы. Рекомендуется явно указывать символы:
ФайлНачало.Записать("--" + Boundary + Символы.ВК + Символы.ПС);
2. Кодировка текстовых фрагментов
Использование КодировкаТекста.ANSI допустимо только для английских имен файлов. Если в filename или значениях полей формы присутствует кириллица, необходимо использовать КодировкаТекста.UTF8, причем желательно без метки BOM, так как лишние байты в начале файла могут повредить структуру запроса.
3. Опечатки в заголовках
Будьте внимательны при ручной установке заголовков HTTPЗапрос. Например, ошибка в слове Content-Length (написание Content-Lenght) приведет к тому, что сервер не поймет размер передаваемых данных. Однако стоит помнить, что при использовании метода УстановитьИмяФайлаТела(), платформа 1С 8.3 самостоятельно вычисляет размер и добавляет этот заголовок.
Начиная с версии 1С 8.3.6, мы можем отказаться от создания множества временных файлов на диске и манипулировать данными прямо в оперативной памяти с помощью объектов Поток и ЗаписьДанных. Это значительно ускоряет работу и повышает надежность кода.
Разберем пример реализации через ПотокВПамяти:
Boundary = СтрЗаменить(Строка(Новый УникальныйИдентификатор()), "-", "");
ПотокТела = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(ПотокТела, КодировкаТекста.UTF8);
// Записываем начало
ЗаписьДанных.ЗаписатьСтроку("--" + Boundary);
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""picture.jpg""");
ЗаписьДанных.ЗаписатьСтроку("Content-Type: image/jpeg");
ЗаписьДанных.ЗаписатьСтроку(""); // Разделитель между заголовком и данными
// Записываем саму картинку
КартинкаДвоичные = Новый ДвоичныеДанные("C:\data\img.jpg");
ЗаписьДанных.Записать(КартинкаДвоичные);
// Записываем финал
ЗаписьДанных.ЗаписатьСтроку("");
ЗаписьДанных.ЗаписатьСтроку("--" + Boundary + "--");
ЗаписьДанных.Закрыть();
// Получаем итоговые данные для запроса
ДанныеДляОтправки = ПотокТела.ЗакрытьИПолучитьДвоичныеДанные();
HTTPЗапрос = Новый HTTPЗапрос("/api/upload");
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ДанныеДляОтправки);
HTTPСоединение = Новый HTTPСоединение("mysite.com");
Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Если код в 1С кажется верным, но сайт все равно выдает ошибку, проанализируем ситуацию с помощью инструментов отладки (Proxy-серверов), таких как Fiddler или Charles. Сравните запрос, который формирует Postman (если в нем загрузка работает), с запросом из 1С. Основное внимание уделите:
Boundary.Content-Type внутри тела и началом двоичных данных.Выяснив причину через сравнение «сырых» HTTP-пакетов, вы сможете легко поправить код формирования тела запроса в 1С. Помните, что веб-сервер крайне чувствителен к структуре multipart/form-data, и даже один пропущенный символ перевода строки может сделать весь файл нечитаемым для принимающей стороны.