Загрузка файла из произвольной WEB-формы через HTTP-сервис 1С на сервер.

18.07.18

Интеграция - WEB-интеграция

Использование буфера двоичных данных.

Казалось бы, нет ничего проще, создать HTTP-сервис с методом POST, создать простейшую web-страничку с формой для загрузки файла. Например, вот такую:

<form enctype="multipart/form-data" method="post" action="http://serv1c/base/hs/test/load">
<p>
<input type="file" name="f">
<input type="submit" value="Отправить">
</p>
</form>

Открыть страничку в браузере, выбрать требуемый файл, нажать на форме кнопку «Отправить». А в HTTP-сервисе выполнить функцию ПолучитьТелоКакДвоичныеДанные и сохранить эти двоичные данные в файл в любое доступное место. Но! Не тут-то было.

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


-----------------------------7e22f312a0802
Content-Disposition: form-data; name="f"; filename="myfile.png"
Content-Type: image/png
‰PNGIHDRxtyx›sRGB®ОйgAMA±Џ ьaPLTEяяяgvмc pHYsWU\) ~NrIDAThCнЌKv$7=чїґP&:‰"UТі—‹О-эПwьЉ•e{UН©KЮцїьяя1f’©?еvп }=Hэ)·{чилAкO№Э»G_RКнЮ=ъ1Вy*аAссЬНнС·ОSЉЏ·аnnЏѕЌpћ xP|јws{фm„уTАѓвг-ё›ЫЈo#њS“Ь`z°ЇR<ъ6В95Й ¦ы*ЕЈo#њS“Ь`z°ЇR<ъ6В95Й ¦ы*ЕЈo#њїлН-џЁг…вз>KpFНМ@йOФсBсsџ%8ЈЙm#ѓв†¦FхWЯF8ЈЙm#ѓв†¦FхWЯF8ЈЙm#ѓв†¦FхWЯF8ЈЙm#ѓв†¦FхWЯF
-----------------------------7e22f312a0802—

Как говорится, «все смешалось в доме Облонских» и текстовая информация и двоичная, которую получить из, условно говоря, гибридного содержимого, средствами 1С - затея так себе. Но благодаря появлению в платформе возможностей работы с буфером двоичных данных можно закатав рукава решить эту задачу.


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


Итак, для начала нужно из заголовка основного web-запроса получить уникальную строку-разделитель (в файле-примере это "---------------------------7e22f312a0802"). Она нужна, в первую очередь для разделения получаемой информации, указанной на форме. В данной статье рассматривается форма для загрузки только одного файла, но ведь на форме могут быть еще и другие поля ввода, которые и разделяются уникальной строкой-разделителем.


Строку-разделитель следует получить из атрибута «boundary» заголовка «Content-Type» основного запроса, например так:

 

ВзялГраницу="";
Для каждого ВзялЗаголовок Из Запрос.Заголовки Цикл
  Если ВзялЗаголовок.Ключ="Content-Type" Тогда
    ВзялЗначение=ВзялЗаголовок.Значение;
    Поз=СтрНайти(ВзялЗначение, "boundary=");
    Если Поз>0 Тогда
        ВзялГраницу = Сред(ВзялЗначение, Поз+СтрДлина("boundary="));
    КонецЕсли;
  КонецЕсли; 
КонецЦикла;

Далее нам нужно, для успешного отделения двоичных данных от текстовых прочитать полученную информацию из запроса в буфер двоичных данных:

Если ПустаяСтрока(ВзялГраницу)=Ложь Тогда

  ВзялПоток = Запрос.ПолучитьТелоКакПоток();
  ВзялРазмер = ВзялПоток.Размер();
  ВзялБуфер = Новый БуферДвоичныхДанных(ВзялРазмер);
  ВзялПоток.Прочитать(ВзялБуфер, 0, ВзялРазмер);
  ВзялПоток = Неопределено; //освобождаем память

!!! Хотелось бы предупредить, что буфер двоичных данных – это выделенная память на сервере. И чем больше размер буфера, тем больше этой самой памяти расходуется. Следить за размером загружаемых файлов и устанавливать ограничения следует на web-сервере, на котором опубликован http-сервис. И по возможности, в тексте модуля, освобождать память, исключая неиспользуемые буферы!!!

В дальнейшей работе нам понадобится вспомогательный буфер-разделитель. В нем будет храниться последовательность 0x0D, 0x0A или 13,10 или проще говоря «возврат каретки» и «перевод строки». Указанная последовательность является разделителем элементов полученной информации:

БуферРазделитель = Новый БуферДвоичныхДанных(2);
БуферРазделитель.Установить(0,13);
БуферРазделитель.Установить(1,10);

Далее возьмем только «нужную» информацию, которая расположена до последней строки-разделителя:

ВзялБуфер=ВзялБуфер.Разделить(БуферРазделитель.Соединить(ПолучитьБуферДвоичныхДанныхИзСтроки("--"+ВзялГраницу+"--")))[0];

!!! В начале всех строк-разделителей добавлена последовательность 0x0D, 0x0A и строка «--», строка «--» добавлена так же и в конце последней строки-разделителя !!!

Теперь получим массив всех элементов формы, которые, как уже говорилось ранее, разделены строкой-разделителем: 

ВзялМассивЭлементовФормы=ВзялБуфер.Разделить(БуферРазделитель.Соединить(ПолучитьБуферДвоичныхДанныхИзСтроки("--"+ВзялГраницу)));
ВзялБуфер = Неопределено; //чистим память

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

Для каждого ВзялЭлемент Из ВзялМассивЭлементовФормы Цикл

Метаинформация о полученном элементе и его значение отделены так называемой «пустой строкой» или последовательностью 0x0D, 0x0A, 0x0D, 0x0A (13,10,13,10).  Т.е. это «двойной» буфер-разделитель, созданный ранее. И у нас наконец-то появилась возможность отделения двоичной информации от текстовой:

ВзялМассивБуферов = ВзялЭлемент.Разделить(БуферРазделитель.Соединить(БуферРазделитель));
ВзялЭлемент = Неопределено; //свобода памяти!

В результате у нас есть массив, в котором первый элемент (с индексом 0) содержит метаинформацию, а все последующие – предполагаемые данные передаваемого файла. Да, да, все последующие, т.к. «пустая строка» (0x0D, 0x0A, 0x0D, 0x0A или 13,10,13,10), о которой говорилось раньше, может присутствовать в данных самого файла и при разделении она учитывается процессом разделения, разбивающим данные файла на части.


Далее необходимо определить, является ли выбранный элемент формы файлом для загрузки. Для этого из полученной метаинформации извлекаем все заголовки и у каждого ищем атрибут «filename». Заголовки отделены друг от друга уже известной последовательностью 0x0D, 0x0A (13,10), которая хранится в буфере-разделителе:

ВзялЗаголовки=ВзялМассивБуферов[0].Разделить(БуферРазделитель);
ВзялМассивБуферов[0]=Неопределено; //очисти сознание
ВзялИмяФайла="";
Для каждого ВзялЗаголовок Из ВзялЗаголовки Цикл

У нас есть возможность преобразовать двоичные данные в строку, сделаем это:

ВзялСтрокуЗаголовка=ПолучитьСтрокуИзБуфераДвоичныхДанных(ВзялЗаголовок);
Поз=СтрНайти(ВзялСтрокуЗаголовка, "filename=");
Если Поз>0 Тогда

Есть атрибут «filename», значит этот элемент формы хранит в себе загружаемый файл. Теперь нужно получить его имя, выделив его из полного пути к нему и убрав кавычки:
 

  ВзялПолноеИмяФайла=СтрЗаменить(Сред(ВзялСтрокуЗаголовка, Поз+СтрДлина("filename=")),"""","");
  ВзялДлину=СтрДлина(ВзялПолноеИмяФайла);
  Для А=0 По ВзялДлину-1 Цикл
   ВзялСимвол=Сред(ВзялПолноеИмяФайла, ВзялДлину-А, 1);
   Если ВзялСимвол="\" ИЛИ ВзялСимвол="/" Тогда
    Прервать;
   КонецЕсли; 
  ВзялИмяФайла = ВзялСимвол + ВзялИмяФайла;
  КонецЦикла; 
  Прервать;
 КонецЕсли; 
КонецЦикла;

Если получено имя загружаемого файла, значит мы можем его теперь «собрать» и записать в наше надежное место с этим именем:

Если ПустаяСтрока(ВзялИмяФайла)=Ложь Тогда

Поскольку первый элемент массива хранит буфер с метаинформацией, то последующие – буферы двоичных данных файла. Что ж объединим их в один:

  ВзялБуферДанныхФайла=ВзялМассивБуферов[1];
    Для А = 2 По ВзялМассивБуферов.ВГраница() Цикл

Не забываем, что массив был создан из частей, разделенных «пустой строкой» (0x0D, 0x0A, 0x0D, 0x0A или 13,10,13,10) и следовательно, этот разделитель должен быть восстановлен при склейке элементов массива:

      ВзялБуферДанныхФайла = ВзялБуферДанныхФайла.Соединить(БуферРазделитель.Соединить(БуферРазделитель.Соединить(ВзялМассивБуферов[А])));
      ВзялМассивБуферов[А]=Неопределено; //чистые мозги
    КонецЦикла;

Ну вот и все. Данные собраны и теперь их можно записать в файл:
 

    Попытка
      ВзялДанныеФайла = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(ВзялБуферДанныхФайла);
      //здесь следует указать путь для сохранения файла для примера указан диск d: сервера:
      ВзялДанныеФайла.Записать("d:\"+ВзялИмяФайла);
      ВзялДанныеФайла = Неопределено; // просветление сознания
    Исключение

Или не записать. Подобную ситуацию нужно будет изучить детально в каждом конкретном случае:
 

      Ответ.КодСостояния = 400;
      Ответ.Причина = "Ошибка получения двоичных данных файла из запроса.";
    КонецПопытки;
    КонецЕсли;
  КонецЦикла; 
Иначе

Web-форма должна быть обязательно с указанным enctype="multipart/form-data" иначе неизбежно будет ситуация ниже.

  Ответ.КодСостояния = 400; 
  Ответ.Причина = "Ошибка получения границы-разделителя для multipart/form-data.";
КонецЕсли; 

   
Платформа 1С:Предприятие 8.3 (8.3.11.3034).

 

post загрузка файла http-сервис

См. также

Интеграция Альфа Авто 5 / Альфа Авто 6 и AUTOCRM / Инфотек

Сайты и интернет-магазины WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме. Без существенных изменений типовой конфигурации. Проверено с брендами: Интеграция 1С и GEELY Интеграция 1С и HAVAL Интеграция 1С и KIA Интеграция 1С и FORD Интеграция 1С и LADA ГАРАНТИЯ 100% ВНЕДРЕНИЯ!

36000 руб.

03.08.2020    15660    9    17    

9

Модуль для обмена "1С:Предприятие 8. УАТ. ПРОФ" с FortMonitor

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ, места стоянок по геозонам. 3. Отчеты по данным загруженным в регистры сведений. 4. Предусмотрена автоматическая загрузка данных в фоновом режиме (условия работы данной загрузке читайте в описании товара) Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен. Любой заинтересованный пользователь, имеет возможность скачать демо-версию расширения.

22656 руб.

25.05.2021    12809    30    8    

10

Интеграция 1С — Битрикс24. Обмен задачами

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс24. Разработка имеет двухстороннюю синхронизацию 1С и Битрикс24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (8.3.18.1289). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    17425    6    15    

13

[Расширение] БОР-Навигатор.Культура

Зарплата Бюджетный учет WEB-интеграция Обмен с ГосИС Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бюджетный учет Платные (руб)

Расширение конфигурации, включающее в себя объекты, необходимые для подготовки и сдачи отчета "Штатная численность" системы "БОР-Навигатор.Культура" в программе "1С:Зарплата и кадры государственного учреждения", редакция 3.1.

8400 руб.

01.02.2019    25687    9    0    

7

Интеграция с сервисом vetmanager

WEB-интеграция Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Бытовые услуги, сервис Платные (руб)

Внешняя обработка разрабатывалась для загрузки документов из Ветменеджер в 1С: Бухгалтерия 3.0

12000 руб.

02.02.2021    16259    41    49    

22
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. EmpireSer 18.07.18 22:32 Сейчас в теме
Вообще заголовки должны читаться не зависимо от регистра (а в 1С в программном коде поиск регистрозависимый). Это написано в стандартах описывающих протокол HTTP.
Тоже самое и о параметрах в заголовке "Content-Type": RFC 2045. Так же там написано, что параметр может быть в кавычках, а может и не иметь их. Для них там только прописан разделить ; (точка с запятой). Так что "filename" в одном из браузеров (забыл в каком) будет без кавычек. Но у тебя делается "СтрЗаменить", хотя для Линукса двойная кавычка - это легальный символ, поэтому кавычки должны проверяться относительно начала и конца строки, а не "СтрЗаменить".
2. bobank 200 19.07.18 07:11 Сейчас в теме
(1) Да, я в курсе этого. Хотя никогда и не встречал на практике "гуляние" регистра символов в заголовках и параметрах. Ведь здесь я не описывал законченное решение. Лишь поделился как можно использовать буфер двоичных данных для выполнения более-менее практической задачи.
3. kembrik 10 14.12.18 17:03 Сейчас в теме
Спасибо, конец мучениям!
4. LaninaNata 77 06.08.20 07:16 Сейчас в теме
Спасибо!!! Именно то что искала!
5. vakham 19 19.10.22 12:02 Сейчас в теме
Возможно, глупый вопрос, но уже забодался с новой для себя темой... Оно так должно в простом виде работать?
&НаСервере
Процедура СохранениеФайла(ДвоичныеДанные)
ДвоичныеДанные.Записать("E:\del_me.txt");
КонецПроцедуры

Функция КоординатыGET(Запрос)
ДвоичныеДанные=Запрос.ПолучитьТелоКакДвоичныеДанные();
СохранениеФайла(ДвоичныеДанные);

Ответ = Новый HTTPСервисОтвет(200);Ответ.УстановитьТелоИзСтроки("ok");Ответ.Заголовки.Вставить("Content-type", "application/json");Возврат Ответ;
КонецФункции
6. EmpireSer 19.10.22 15:31 Сейчас в теме
(5)

1. Вот тут не правильно точно:
Ответ.Заголовки.Вставить("Content-type", "application/json");
и
Ответ.УстановитьТелоИзСтроки("ok");

Вы указываете, что ответ будет в формате JSON, но отправляете не JSON, а просто текст. JSON выглядит по другому.
---
2. А что вы хотите? Тема касается именно особой операции, которую когда-то придумали при начале появления интернета и сайтов, чтобы загружать файлы с клиента на сервер. При обычной работе всяких API (SOAP (или как его в 1С кличат Web) и HTTP) сервисах редко делают так, а если и делают, то обычно это или наследие какого-то фреимворка или для совместимости/удобства каких-то сайтов, которые тоже с этим API работают)
7. vakham 19 19.10.22 15:49 Сейчас в теме
(6) Спасибо за ответ и уточнение. У меня стоит задача получения 1С-кой данных, включая файлы. Между web-сервисами и http-сервисами 1С выбрал http-сервис (не знаю, на сколько это оправдано). Файл в "зашумлённом" виде наконец удалось получить. Теперь вот думаю "вычищать" с как в публикации или есть более новые методы?
8. EmpireSer 20.10.22 17:04 Сейчас в теме
(7)
...получения 1С-кой данных, включая файлы.

Это не полная задача. Кто отправляет данные? Сайт (сама web страница или бэкенд?), другой сервер (питон, .NET, JavaScript, другая 1с) ?.
У вас явно другая история возникновения данных, так как вы зачем-то делаете тело ответа, а не просто отправляете HTTP 200.

P.S. Просто из-за того, что вы написали "но уже забодался с новой для себя темой" очень высока вероятность, что вы просто сами себе создаёте проблемы. Тут нет одного пути решения, у каждого есть плюсы и минусы. В теме решена именно конкретная задача, когда данные файла отправляются как один из блоков тела HTTP вызова и самый явный такой случай - данные перехватываются самой 1С как бэкенда сайта (т.е. например сайт сам написан внутри конфигурации 1С (пример такого сайта есть на инфостарте)).
В остальном условия, когда вам надо именно так, и ни как иначе, ловить и разбирать такие данные, довольно редки.
9. vakham 19 24.10.22 12:58 Сейчас в теме
(8) Дело в том, что пока не готова программа (сайт), которая будет отсылать. Даже протокола нет. Поэтому тестировал Postman-ом.
И да, возможно создаю проблемы, ибо впервые этой темой на 1С8 занимаюсь. Как ещё ловить файлы и данные банально не нашёл решения.
10. EmpireSer 24.10.22 16:47 Сейчас в теме
(9) Если будет сайт отдельно и 1С отдельно, то лучше всего не создавать API с таким функционалом... Это лишние проблемы на стороне 1С. Тем самым сайт будет к себе загружать файлы своими средствами (благо в JavaScript, PHP и других нормальный языках есть удобные методы всё это обработать), а 1С будет файлы получать, вызывая такое API, где тело ответа и есть всё содержимое файла (т.е. без лишних конструкций и блоков). Например там файлы вы загружаете с инфостарта, когда он ссылки для скачивания присылает на почту (по крайней мере раньше так было и я специально давно сохранил RAW-ы HTTP запроса и ответа в браузере, чтобы потом что-то аналогичное создать самому)

Но если бэком будет сама 1С, то вам придётся это изучить. Тут могу только посочувствовать.

P.S. Вообще лучше всего помогает анализировать RAW дампы HTTP запросов и ответов, которые формирует браузер, когда происходят интересные операции на сайтах. Есть много сайтов, где можно что-то скачать или загрузить что-то на сайт. Яндекс.Диск, Гугл.Диск, бесплатные фостинги картинок и т.п. Сохраните что там происходит и гуглите непонятные слова.
Это конечно не быстро, надо запастись терпением, но если эти знания вам нужны не на "как-то сделать и забыть", а на будущее, то лучше всего поглубже погрузиться в вопрос.
Оставьте свое сообщение