Загрузка файла из произвольной 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 разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    15933    13    18    

13

Интеграция 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    17889    6    15    

12

Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС

Обмен с ГосИС WEB-интеграция Платформа 1С v8.3 Управляемые формы 1С:Комплексная автоматизация 1.х 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия государственного учреждения 1С:Документооборот 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Платные (руб)

Обработка является альтернативой механизму, разработанному фирмой 1С и заполняющему реквизиты контрагента по ИНН или наименованию. Не требуется действующей подписки ИТС. Вызывается как внешняя дополнительная обработка, т.е. используется, непосредственно, из карточки контрагента. Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС (egrul.nalog.ru) для БП 2.0, БП 3.0, БГУ 1.0, БГУ 2.0, УТ 10.3, УТ 11.x, КА 1.1, КА 2.x, УПП 1.x, ERP 2.x, УНФ 1.5, УНФ 1.6, УНФ 3.0, ДО 2.1

2400 руб.

28.04.2016    88945    163    216    

318

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

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

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

8400 руб.

01.02.2019    25874    9    0    

7

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

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

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

12000 руб.

02.02.2021    16479    42    49    

23
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
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 21 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 21 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 21 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 запросов и ответов, которые формирует браузер, когда происходят интересные операции на сайтах. Есть много сайтов, где можно что-то скачать или загрузить что-то на сайт. Яндекс.Диск, Гугл.Диск, бесплатные фостинги картинок и т.п. Сохраните что там происходит и гуглите непонятные слова.
Это конечно не быстро, надо запастись терпением, но если эти знания вам нужны не на "как-то сделать и забыть", а на будущее, то лучше всего поглубже погрузиться в вопрос.
Оставьте свое сообщение