При разработке HTTP-сервисов в среде 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда стороннее приложение или веб-сайт присылает запрос, содержащий одновременно и текстовую информацию (например, статус или комментарий), и бинарные данные (например, изображение в формате JPEG). В этой статье мы подробно разберем, как правильно обрабатывать такие запросы, почему стандартный метод чтения строки может не подойти и какие современные инструменты платформы следует использовать для эффективного решения задачи.
Представим ситуацию: на ваш HTTP-сервис приходит POST-запрос. В теле запроса передаются три элемента: поле status, поле comment и файл image.jpg. Если мы попытаемся прочитать тело запроса простым методом Запрос.ПолучитьТелоКакСтроку(), мы увидим текстовую "кашу", в которой перемешаны служебные заголовки, границы разделов (boundary) и нечитаемые символы бинарного файла.
Для начала проанализируем, какие методы предоставляет объект HTTPСервисЗапрос:
ПолучитьТелоКакСтроку() — идеально подходит для JSON или XML, но "ломает" бинарные данные при попытке интерпретировать их как текст.ПолучитьТелоКакДвоичныеДанные() — возвращает всё тело целиком в виде объекта ДвоичныеДанные. Это безопасный способ, сохраняющий целостность картинки, но требующий последующего ручного разбора.Разберем, почему данные приходят в таком виде. Когда клиент отправляет форму с файлом, используется заголовок Content-Type: multipart/form-data; boundary=----WebKitFormBoundary.... Это означает, что тело запроса разделено на части специальной строкой-разделителем.
Прежде чем приступать к чтению тела, нам необходимо выяснить, как именно упакованы данные. Рассмотрим пример кода для анализа заголовков:
// В методе HTTP-сервиса (например, POST)
ТипСодержимого = Запрос.Заголовки.Получить("Content-Type");
Если СтрНайти(ТипСодержимого, "multipart/form-data") > 0 Тогда
// Мы имеем дело со смешанными данными
// Нужно найти разделитель (boundary)
ПозицияГраницы = СтрНайти(ТипСодержимого, "boundary=");
Разделитель = Сред(ТипСодержимого, ПозицияГраницы + СтрДлина("boundary="));
КонецЕсли;
Знание boundary критически важно, так как именно эта строка отделяет ваш статус от комментария, а комментарий от тела картинки.
Начиная с версии платформы 8.3.10, в 1С появились мощные инструменты для работы с потоками. Проанализируем ситуацию: если файл картинки весит несколько мегабайт, считывание его целиком в память как ДвоичныеДанные может быть неэффективным. Вместо этого мы воспользуемся объектом ЧтениеДанных.
Рассмотрим алгоритм работы с потоком тела запроса:
// Получаем поток из тела запроса
ПотокТела = Запрос.ПолучитьТелоКакПоток();
Чтение = Новый ЧтениеДанных(ПотокТела);
// В реальной задаче здесь должен быть цикл по частям multipart
// Для упрощения разберем логику получения конкретной части
Пока Истина Цикл
// Читаем данные до разделителя
// Это сложный процесс парсинга MIME-заголовков каждой части
Прервать; // Условный выход
КонецЦикла;
Поскольку встроенного автоматического парсера multipart/form-data для HTTP-сервисов в 1С "из коробки" (в одну команду) не существует, нам придется выполнить следующие действия:
Content-Disposition: form-data; name="status").Выясним причину, по которой это лучше делать через ДвоичныеДанные и Поток: если вы переведете поток в строку для поиска разделителя, вы безвозвратно повредите байты изображения. Поэтому поиск разделителя нужно выполнять либо побайтово, либо используя временные файлы.
Рассмотрим более детальный пример, когда мы точно знаем структуру запроса. Допустим, мы решили прочитать всё тело как ДвоичныеДанные и затем извлечь из него картинку.
ТелоДанные = Запрос.ПолучитьТелоКакДвоичныеДанные();
// В данном случае мы можем воспользоваться объектом РазделениеДвоичныхДанных
// или сохранить во временный файл и работать с ним через ЧтениеДанных
ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
ТелоДанные.Записать(ИмяВременногоФайла);
ФайлПоток = Новый ФайловыйПоток(ИмяВременногоФайла, РежимОткрытияФайла.Открыть);
Чтение = Новый ЧтениеДанных(ФайлПоток);
// Читаем данные порциями, ищем boundary
// При нахождении заголовка картинки "image/jpeg"
// Сохраняем последующие данные в новый файл до следующего boundary
Важный момент: Если данные передаются в формате JSON, но внутри JSON одно из полей содержит картинку, она будет передана в кодировке Base64. В этом случае алгоритм гораздо проще:
// Если Content-Type: application/json
СтрокаJSON = Запрос.ПолучитьТелоКакСтроку();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
СтруктураДанных = ПрочитатьJSON(ЧтениеJSON);
Статус = СтруктураДанных.status;
Комментарий = СтруктураДанных.comment;
ДвоичныеДанныеКартинки = Base64Значение(СтруктураДанных.image_base64);
Проанализируем ситуацию, когда картинка успешно получена. Что с ней делать дальше? В 1С есть несколько вариантов хранения:
Для загрузки полученных ДвоичныхДанных в справочник, используем стандартный метод:
НовыйОбъект = Справочники.НоменклатураПрисоединенныеФайлы.СоздатьЭлемент();
НовыйОбъект.ФайлХранилище = Новый ХранилищеЗначения(ДвоичныеДанныеКартинки);
НовыйОбъект.Наименование = "ЗагруженноеИзображение.jpg";
НовыйОбъект.Записать();
Мы рассмотрели основные способы получения данных из тела HTTP-запроса. Подведем итоги:
Content-Type в заголовках запроса.ПолучитьТелоКакДвоичныеДанные() или ПолучитьТелоКакПоток().multipart/form-data используйте новые инструменты платформы: ЧтениеДанных, ПотокДанных и БуферДвоичныхДанных.ПолучитьТелоКакСтроку(), так как это приведет к порче бинарных данных.Используя эти подходы, вы сможете корректно принимать данные от любых внешних систем, будь то мобильные приложения, веб-формы или сторонние API.