Рассмотрим ситуацию, с которой часто сталкиваются разработчики при интеграции 1С со сторонними веб-сервисами. Допустим, стоит задача отправить файл (например, изображение) через API с помощью POST-запроса. Техническая поддержка сервиса обычно предоставляет готовые примеры на PHP, где для отправки файла используется библиотека cURL и встроенная функция curl_file_create(). Разберем по шагам, как перенести эту логику в 1С, выясним причину популярных ошибок и научимся правильно формировать тело запроса.
Часто программисты пытаются передать параметры и сам файл, упаковывая их в структуру и преобразуя в JSON, при этом указывая заголовок, предназначенный для отправки файлов. Посмотрим на пример такого ошибочного кода:
Сайт = "media.sms-online.com";
Parameters = Новый Структура;
Parameters.Вставить("login", Логин);
Parameters.Вставить("image", Картинка); // Ошибка: сюда пытаются поместить двоичные данные или путь
Parameters.Вставить("sign", ХешСигн);
HTTPСоединение = Новый HTTPСоединение(Сайт,,,,,,Новый ЗащищенноеСоединениеOpenSSL);
HTTPЗапрос = Новый HTTPЗапрос("/upload/");
Запись = Новый ЗаписьJSON;
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет, " ", Истина);
Запись.УстановитьСтроку(ПараметрыJSON);
ЗаписатьJSON(Запись, Parameters);
СтрокаJSON = Запись.Закрыть();
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data");
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
ОтветHTTP = HTTPСоединение.Post(HTTPЗапрос);
В результате сервер возвращает ошибку: Mandatory parameters missing: image (Обязательный параметр отсутствует: image). Выясним причину такого поведения.
Главная ошибка заключается в противоречии форматов. В 1С формируется тело запроса в формате JSON с помощью метода ЗаписатьJSON, но при этом в заголовке Content-Type указывается значение multipart/form-data. Сервер, читая заголовок, ожидает получить структуру, разделенную специальными границами (boundary), а вместо этого получает обычную текстовую строку JSON. Естественно, он не может найти в ней файл и выдает ошибку. Передача двоичных данных или Base64-строки внутрь JSON здесь не поможет, так как принимающая сторона ожидает классическую HTML-форму отправки файла.
Чтобы понять, как реализовать отправку в 1С, проанализируем, как это работает в PHP. Объект CURLFile сообщает библиотеке cURL, что запрос должен быть отправлен именно как форма (multipart). Под капотом система делает огромную работу: автоматически генерирует уникальный разделитель (Boundary), формирует бинарное тело запроса, прописывает метаданные файла (имя, MIME-тип) и подставляет сырые двоичные данные. В платформе 1С:Предприятие нет встроенной "одной кнопки" для создания такой структуры, поэтому весь пакет данных нам придется собрать вручную.
Рассмотрим алгоритм ручной сборки тела запроса. Нам потребуется использовать объекты ПотокВПамяти и ЗаписьДанных, чтобы корректно склеить текстовые параметры и бинарные данные картинки.
image/jpeg). Далее записываются двоичные данные картинки.Разберем готовый шаблон кода, который решает поставленную задачу. Этот код склеивает данные в правильном бинарном формате.
// 1. Генерируем уникальный разделитель (Boundary)
УИД = Строка(Новый УникальныйИдентификатор());
Boundary = "----1CBoundary" + СтрЗаменить(УИД, "-", "");
// 2. Инициализируем поток для сборки тела запроса
ПотокТела = Новый ПотокВПамяти;
ЗаписьДанных = Новый ЗаписьДанных(ПотокТела, КодировкаТекста.UTF8);
// Символы переноса строки, требуемые стандартом
ПереносСтроки = Символы.ВК + Символы.ПС;
// 3. Функция/Процедура добавления текстового параметра (псевдокод для понимания структуры)
// Добавляем параметр "login"
ТекстПараметра = "--" + Boundary + ПереносСтроки +
"Content-Disposition: form-data; name=""login""" + ПереносСтроки +
ПереносСтроки +
ЛогинПользователя + ПереносСтроки;
ЗаписьДанных.Записать(ТекстПараметра);
// Добавляем параметр "sign"
ТекстПараметра = "--" + Boundary + ПереносСтроки +
"Content-Disposition: form-data; name=""sign""" + ПереносСтроки +
ПереносСтроки +
ХешСигн + ПереносСтроки;
ЗаписьДанных.Записать(ТекстПараметра);
// 4. Добавляем файл (картинку)
ТекстЗаголовкаФайла = "--" + Boundary + ПереносСтроки +
"Content-Disposition: form-data; name=""image""; filename=""photo.jpg""" + ПереносСтроки +
"Content-Type: image/jpeg" + ПереносСтроки +
ПереносСтроки;
ЗаписьДанных.Записать(ТекстЗаголовкаФайла);
// Записываем сами двоичные данные файла
ДДКартинки = Новый ДвоичныеДанные(ПутьККартинке);
ЗаписьДанных.Записать(ДДКартинки);
ЗаписьДанных.Записать(ПереносСтроки); // Перенос после файла
// 5. Завершаем тело запроса финальным разделителем
ФинальныйРазделитель = "--" + Boundary + "--" + ПереносСтроки;
ЗаписьДанных.Записать(ФинальныйРазделитель);
ЗаписьДанных.Закрыть();
// Получаем итоговые двоичные данные из потока
ДвоичныеДанныеТела = ПотокТела.ЗакрытьИПолучитьДвоичныеДанные();
// 6. Формируем HTTP-запрос и отправляем
HTTPСоединение = Новый HTTPСоединение("media.sms-online.com",,,,,, Новый ЗащищенноеСоединениеOpenSSL);
HTTPЗапрос = Новый HTTPЗапрос("/upload/");
// ВАЖНО: Указываем сгенерированный Boundary в заголовке
HTTPЗапрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеТела);
ОтветHTTP = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
Проанализируем этот код. Мы отказались от использования JSON и собираем запрос по правилам стандарта RFC для HTML-форм. Использование объектов ПотокВПамяти и ЗаписьДанных позволяет легко комбинировать строковые данные (заголовки, имена параметров) и чистые бинарные данные картинки, не искажая их кодировкой. Обратите особое внимание на расстановку переносов строк (комбинация Символы.ВК + Символы.ПС) — серверы очень чувствительны к их правильному расположению. Также критически важно передать сгенерированный Boundary в заголовок Content-Type запроса, иначе сервер не поймет, где начинаются и заканчиваются блоки данных.