Как реализовать отправку данных multipart/form-data из веб-клиента 1С:Фреш?

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

При разработке интеграций в среде 1С часто возникает задача передачи файлов на сторонние ресурсы с использованием протокола HTTP и формата multipart/form-data. Особенно остро этот вопрос встает при работе в облачных решениях, таких как 1С:Фреш, где пользователи преимущественно используют веб-клиент. Разберем типичную ситуацию: код прекрасно работает в тонком клиенте, но выдает «Ошибку установки соединения» в браузере. Проанализируем причины этого поведения и найдем правильный способ реализации.

Выясняем причину ошибки: почему веб-клиент блокирует соединение?

Рассмотрим ситуацию, когда при выполнении метода ОтправитьДляОбработкиАсинх в веб-клиенте возникает исключение. Основная причина кроется не в ошибке кода 1С, а в архитектуре безопасности современных веб-браузеров. Проанализируем механизм Same-Origin Policy (SOP). Это политика одного источника, которая запрещает скрипту, загруженному с одного домена (например, 1cfresh.com), обращаться к ресурсам на другом домене (например, api.myservice.ru).

Когда мы запускаем 1С в браузере, платформа становится «заложником» этих правил. В Синтакс-помощнике для объекта HTTPСоединение в секции «Примечание» четко указано, что в веб-клиенте запросы могут быть ограничены. Если целевой сервер не настроен специальным образом (не отдает заголовки CORS), браузер просто обрывает соединение, что 1С интерпретирует как ошибку установки связи.

Ручное формирование тела multipart/form-data (старый метод)

Проанализируем пример кода, который часто используют разработчики для формирования многокомпонентного сообщения вручную. Этот метод основан на прямой записи данных в поток. Рассмотрим, как это выглядит программно:


&НаКлиенте
Асинх Процедура ОтправитьФайлНаКлиенте1(Текст)
    ИмяВторогоПараметра = "catalog_code";
    Разделитель = СтрЗаменить(Новый УникальныйИдентификатор, "-", "");
    Тело = Новый ПотокВПамяти;
    ЗаписьДанных = Новый ЗаписьДанных(Тело, КодировкаТекста.UTF8);
    
    // Формируем первую часть (файл)
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("--" + Разделитель);
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("Content-Disposition: form-data; name=""catalog_file""; filename=""data.csv""");
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("Content-Type: text/csv");
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх(""); // Пустая строка перед данными
    Ждать ЗаписьДанных.ЗаписатьАсинх(ПолучитьДвоичныеДанныеИзСтроки(Текст, КодировкаТекста.UTF8));
    
    // Формируем вторую часть (параметр)
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("--" + Разделитель);
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("Content-Disposition: form-data; name=" + ИмяВторогоПараметра + ";");
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("");
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("my_value");
    
    // Завершаем сообщение
    Ждать ЗаписьДанных.ЗаписатьСтрокуАсинх("--" + Разделитель + "--");
    Ждать ЗаписьДанных.ЗакрытьАсинх();
    
    ДанныеТела = Тело.ЗакрытьИПолучитьДвоичныеДанные();
    
    Заголовки = Новый Соответствие;
    Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Разделитель);
    
    HTTPЗапрос = Новый HTTPЗапрос("/api/v1/upload/", Заголовки);
    HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(ДанныеТела);
    
    // ИМЕННО ЗДЕСЬ В ВЕБ-КЛИЕНТЕ БУДЕТ ОШИБКА
    Соединение = Новый HTTPСоединение("myserver.ru", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
    Результат = Ждать Соединение.ОтправитьДляОбработкиАсинх(HTTPЗапрос);
КонецПроцедуры

Хотя этот код технически верен для тонкого клиента, он не решит проблему в браузере. Кроме того, ручное управление границами (boundary) и переносами строк Символы.ВК + Символы.ПС часто приводит к ошибкам на стороне принимающего сервера.

Использование современного объекта HTTPЗапросПараметрМногокомпонентногоСообщения

Начиная с версии платформы 8.3.15, мы можем использовать штатные средства 1С для работы с multipart/form-data. Это избавляет нас от необходимости вручную вычислять boundary. Посмотрим на этот подход подробнее:

Объект HTTPЗапросПараметрМногокомпонентногоСообщения позволяет добавлять части сообщения (файлы или текстовые поля) как отдельные элементы коллекции. Платформа сама расставит разделители и сформирует правильные заголовки.


ПараметрыЗапроса = Новый Массив;

// Часть с файлом
ЧастьФайл = Новый HTTPЗапросПараметрМногокомпонентногоСообщения;
ЧастьФайл.Имя = "catalog_file";
ЧастьФайл.ИмяФайла = "catalog.csv";
ЧастьФайл.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеФайла);
ПараметрыЗапроса.Добавить(ЧастьФайл);

// Часть с текстовым полем
ЧастьТекст = Новый HTTPЗапросПараметрМногокомпонентногоСообщения;
ЧастьТекст.Имя = "catalog_code";
ЧастьТекст.УстановитьТелоИзСтроки("my_value");
ПараметрыЗапроса.Добавить(ЧастьТекст);

HTTPЗапрос = Новый HTTPЗапрос("/api/v1/upload/");
HTTPЗапрос.СформироватьТелоМногокомпонентногоСообщения(ПараметрыЗапроса);

Решение проблемы для 1С:Фреш: перенос логики на сервер

Для того чтобы обойти ограничения безопасности браузера и успешно пройти аудит в 1С:Фреш, нам необходимо перенести выполнение HTTP-запроса на сторону сервера. Выясним, как это сделать правильно:

  1. На стороне клиента: Получаем файл от пользователя (например, через ПоместитьФайлНаСервер) и передаем адрес временного хранилища в серверную процедуру.
  2. На стороне сервера: Извлекаем данные из временного хранилища, формируем HTTPЗапрос (желательно новым способом через параметры многокомпонентного сообщения) и выполняем HTTPСоединение.
  3. Прохождение аудита: В 1С:Фреш запрещены произвольные HTTP-вызовы. Вам потребуется задекларировать внешнее соединение в профиле безопасности вашего расширения. Если адрес ресурса статический, это не вызовет проблем при проверке.

Проанализируем схему взаимодействия:


&НаКлиенте
Процедура ОтправитьФайл(Команда)
    // 1. Выбираем файл на диске пользователя
    // 2. Помещаем во временное хранилище
    // 3. Вызываем серверный метод
    Результат = ОтправитьДанныеНаСервере(АдресХранилища);
КонецПроцедуры

&НаСервере
Функция ОтправитьДанныеНаСервере(АдресХранилища)
    Данные = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    // Здесь выполняем всю работу с HTTPСоединение
    // На сервере нет ограничений SOP браузера!
    Соединение = Новый HTTPСоединение("api.target-server.com", 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
    
    // Формируем запрос...
    // Отправляем...
    Возврат РезультатЗапроса;
КонецФункции

Подведем итоги

Если ваша задача — заставить работать multipart/form-data в веб-клиенте, помните о следующих правилах:

Разделив логику на клиентскую (подготовка данных) и серверную (отправка), вы получите надежное решение, которое будет работать одинаково стабильно и в тонком клиенте, и в любом современном браузере.

← На главную