Хочу поделиться алгоритмом по разбору http сообщений multipart/form-data, который позволяет обрабатывать данные сообщения на порядок быстрее аналогов. Пример аналога можно взять с ИТС (https://its.1c.ru/db/metod8dev/content/5917/hdoc). В данном примере интересует метод
&НаСервере
Функция ПрочитатьСообщение(заголовки, тело)
Разделитель = ПолучитьРазделительСоставногоСообщения(заголовки);
Маркеры = Новый Массив();
Маркеры.Добавить("==" + Разделитель);
Маркеры.Добавить("==" + Разделитель + Символы.ПС);
Маркеры.Добавить("==" + Разделитель + Символы.ВК);
Маркеры.Добавить("==" + Разделитель + Символы.ВК + Символы.ПС);
Маркеры.Добавить("==" + Разделитель + "==");
Текст = Неопределено;
Изображение1 = Неопределено;
Изображение2 = Неопределено;
ЧтениеДанных = Новый ЧтениеДанных(Тело);
// Переходим к началу первой части
ЧтениеДанных.ПропуститьДо(Маркеры);
// Далее в цикле читаем все части
Пока Истина Цикл
Часть = чтениеДанных.ПрочитатьДо(Маркеры);
Если Не Часть.МаркерНайден Тогда
// Неправильно сформированное сообщение
Прервать;
КонецЕсли;
ЧтениеЧасти = Новый ЧтениеДанных(Часть.ОткрытьПотокДляЧтения());
ЗаголовкиЧасти = ПрочитатьЗаголовки(ЧтениеЧасти);
ИмяЧасти = ПолучитьИмяСообщения(ЗаголовкиЧасти);
Если ИмяЧасти = "MessageText" Тогда
Текст = чтениеЧасти.ПрочитатьСимволы();
ИначеЕсли ИмяЧасти = "image1" Тогда
Изображение1 = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();
ИначеЕсли ИмяЧасти = "image2" Тогда
Изображение2 = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();
КонецЕсли;
Если Часть.ИндексМаркера = 4 Тогда
// Прочитали последнюю часть
Прервать;
КонецЕсли;
КонецЦикла;
Возврат Новый Структура("Сообщение,Картинка1,Картинка2",
Текст,
Изображение1,
Изображение2);
КонецФункции
Данный алгоритм, если кратко, строится на чтении потоков двоичных данных (более подробно можно в самой статье на ИТС). Данный вариант рабочий, но достаточно медленный, на обработку всего сообщения уходит чуть больше 10 секунд.
В алгоритме же, которым я хочу поделиться, все строится на:
- Вместо строковых маркеров используем помещение строк в буферы двоичных данных (реализовано таким образом потому что разделить буфер двоичных данных можно только с помощью других буферов);
- Выполнение чтения двоичных данных тела сообщения;
- Пропуска до первой части;
- Читаем двоичные данные в 1 общий буфер двоичных данных;
- Разделяем буфер с помощью маркеров и получаем массив буферов двоичных данных с нужными нам частями;
- Выполняем обход массива, при выполнении цикла открываем поток в оперативной памяти;
- Далее выполняем чтение потока в оперативной памяти;
- Закрываем чтение потока;
- Закрываем поток.
Сам алгоритм в коде выглядит следующим образом:
&НаСервере
Функция ПрочитатьСообщение(заголовки, тело)
Разделитель = ПолучитьРазделительСоставногоСообщения(заголовки);
Маркеры = Новый Массив();
Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("==" + Разделитель));
Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("==" + Разделитель + Символы.ПС));
Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("==" + Разделитель + Символы.ВК));
Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("==" + Разделитель + Символы.ВК + Символы.ПС));
Маркеры.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("==" + Разделитель + "=="));
текст = Неопределено;
изображение1 = Неопределено;
изображение2 = Неопределено;
ЧтениеДанных = Новый ЧтениеДанных(тело);
// Переходим к началу первой части
ЧтениеДанных.ПропуститьДо(Маркеры);
//Помещаем в общий буфер двоичных данных и разделяем буфер с помощью маркеров
ОбщийБуферДвоичныхДанных = ЧтениеДанных.ПрочитатьВБуферДвоичныхДанных();
БуферыДвоичныхДанных = ОбщийБуферДвоичныхДанных.Разделить(Маркеры);
// Далее в цикле читаем все буферы
Для Каждого Буфер Из БуферыДвоичныхДанных Цикл
Поток = новый ПотокВПамяти(Буфер);
ЧтениеЧасти = Новый ЧтениеДанных(Поток);
ЗаголовкиЧасти = ПрочитатьЗаголовки(ЧтениеЧасти);
ИмяЧасти = ПолучитьИмяСообщения(заголовкиЧасти);
Если имяЧасти = "MessageText" Тогда
текст = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();;
ИначеЕсли имяЧасти = "image1" Тогда
изображение1 = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();
ИначеЕсли имяЧасти = "image2" Тогда
изображение2 = ЧтениеЧасти.Прочитать().ПолучитьДвоичныеДанные();
КонецЕсли;
ЧтениеЧасти.Закрыть();
Поток.Закрыть();
КонецЦикла;
текст = ПолучитьСтрокуИзДвоичныхДанных(текст);
Возврат Новый Структура("Сообщение,Картинка1,Картинка2", текст, изображение1, изображение2);
КонецФункции
Выполнение обработки всего сообщения с помощью данного алгоритма занимает всего сотые секунды:
В завершении хотелось бы отметить, что при использовании данного алгоритма происходят манипуляции с оперативной памятью, из-за чего при обработке больших файлов может возникнуть дефицит ресурса ОП.
Надеюсь, данный алгоритм сможет упростить работу с составными сообщениями http.
P.S. для работы алгоритма версия платформы должна быть не ниже 8.3.9.