Отправить *.xlsx файл через HTTP

1. dikd 17 18.02.20 18:26 Сейчас в теме
Привет всем!
Есть API, в который нужно передать экселевский файл...

Написал уже 2мя разными способами, а ответ один и тот же, вернее ошибка: "Должен быть отправлен файл в поле​"
Хотя в поле всё указано, разработчики со стороны API не могут внятно ничем помочь, показали рабочий код на cURL и на это всё.

Может тут у кого идеи возникнут в чём беда:

Итак,
Вариант оправки №1, через ПотокДанных():

	ФайлДанных = "D:/11111111_ACC.xlsx";
	Если ФайлДанных = Неопределено тогда
		Возврат;
	КонецЕсли;
	БинДанные = Новый ДвоичныеДанные(ФайлДанных);
	
	Токен = ПолучитьТокен();
	Соединение = ПолучитьСоединение();	
	
	Boundary = СтрЗаменить(Строка(Новый УникальныйИдентификатор()), "-", "");
    
	ПотокТело = Новый ПотокВПамяти();
	ЗаписьДанных = Новый ЗаписьДанных(ПотокТело); 
	ЗаписьДанных.ЗаписатьСтроку("");
	ЗаписьДанных.ЗаписатьСтроку("--"+Boundary);
	ЗаписьДанных.Записать(СоздатьСообщение_Файл("file", ФайлДанных, БинДанные));
	ЗаписьДанных.ЗаписатьСтроку("--"+Boundary+"--");
	ЗаписьДанных.Закрыть();
	
	ДвоичныеДанныеТело = ПотокТело.ЗакрытьИПолучитьДвоичныеДанные();

	Заголовки = Новый Соответствие;
	Заголовки.Вставить("Accept","application/json");
	Заголовки.Вставить("Authorization","Bearer " + Токен);
	Заголовки.Вставить("Content-Type","multipart/form-data; boundary=" + "--" +boundary + "");
	
	Запрос = Новый HTTPЗапрос("/api/v1/dealers/12345678/accessories/",Заголовки);
	
	Запрос.УстановитьТелоИзДвоичныхДанных(ДвоичныеДанныеТело);	
	
	Ответ = Соединение.ОтправитьДляОбработки(Запрос);
	Результат = Ответ.ПолучитьТелоКакСтроку();


Функция СоздатьСообщение_Файл(ИмяСообщения, ИмяФайла, ДвоичныеДанные)
    
    Поток = Новый ПотокВПамяти();
    ЗаписьДанных = Новый ЗаписьДанных(Поток);
	
	ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=" + ИмяСообщения + "; filename=" + ИмяФайла+"");
    ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/vnd.ms-excel"+ Символы.ПС );    
    ЗаписьДанных.Записать(ДвоичныеДанные);    
    ЗаписьДанных.Закрыть();
    
    Возврат Поток.ЗакрытьИПолучитьДвоичныеДанные();
    
КонецФункции
Показать


Вариант оправки №2, через Объединение файлов:

ФайлДанных = "D:/11111111_ACC.xlsx";
	Если ФайлДанных = Неопределено тогда
		Возврат;
	КонецЕсли;
	БинДанные = Новый ДвоичныеДанные(ФайлДанных);
	
	Токен = ПолучитьТокен();
	Соединение = ПолучитьСоединение();	
	
	Boundary = СтрЗаменить(Строка(Новый УникальныйИдентификатор()), "-", "");

	ИмяФайлаОтправки = КаталогВременныхФайлов() + "post.txt";
	ИмяФайлаДляЗагрузки = ФайлДанных;
	МассивФайловДляОбъединения = Новый Массив; 

	//Формируем начальный фрагмент файла POST-запроса 

	ИмяФайлаОтправкиНачало = ПолучитьИмяВременногоФайла("txt"); 
	ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяФайлаОтправкиНачало, КодировкаТекста.UTF8, Символы.ПС, Ложь); 
	//Определяем раздел двоичных данных 

	ФайлОтправкиНачало.ЗаписатьСтроку("--" + Boundary); 
	//Указываем имя файла для передачи 

	ФайлОтправкиНачало.ЗаписатьСтроку("Content-Disposition: form-data; name=""file""; filename=""" + ИмяФайлаДляЗагрузки + """"); 
	//Указываем тип передаваемых данных. 

	ФайлОтправкиНачало.ЗаписатьСтроку("Content-Type: application/vnd.ms-excel"); 
	ФайлОтправкиНачало.ЗаписатьСтроку(""); 
	ФайлОтправкиНачало.Закрыть(); 

	МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиНачало); 

	//Копируем файл для загрузки во временный и добавляем в массив файлов для объединения 

	ИмяФайлаДляЗагрузкиВременный = ПолучитьИмяВременногоФайла("tmp"); 
	КопироватьФайл(ИмяФайлаДляЗагрузки, ИмяФайлаДляЗагрузкиВременный); 
	МассивФайловДляОбъединения.Добавить(ИмяФайлаДляЗагрузкиВременный); 

	//Формируем конечный фрагмент файла POST-запроса 

	ИмяФайлаОтправкиКонец = ПолучитьИмяВременногоФайла("txt"); 
	ФайлОтправкиКонец = Новый ЗаписьТекста(ИмяФайлаОтправкиКонец, КодировкаТекста.UTF8, Символы.ПС, Ложь); 

	//Завершение раздела двоичных данных 
	ФайлОтправкиКонец.ЗаписатьСтроку(""); 
	ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary); 

	ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary + "--"); 
	ФайлОтправкиКонец.Закрыть(); 

	МассивФайловДляОбъединения.Добавить(ИмяФайлаОтправкиКонец); 
	ОбъединитьФайлы(МассивФайловДляОбъединения, ИмяФайлаОтправки);
	//*************************************
Заголовки = Новый Соответствие;
	Заголовки.Вставить("Accept","application/json");
	Заголовки.Вставить("Authorization","Bearer " + Токен);
	Заголовки.Вставить("Content-Type","multipart/form-data; boundary=" + "--" +boundary + "");
	
	Запрос = Новый HTTPЗапрос("/api/v1/dealers/12345678/accessories/",Заголовки);
	
	Запрос.УстановитьИмяФайлаТела(ИмяФайлаОтправки); 
	
	Ответ = Соединение.ОтправитьДляОбработки(Запрос);
	Результат = Ответ.ПолучитьТелоКакСтроку();

Показать



В обоих случаях возвращается :
"Должен быть отправлен файл в поле​"


Тех. поддержка со стороны API прислал рабочий код на cURL:

curl --location --request POST '/api/v1/dealers/12345678/accessories/' \
--header 'Accept: application/json' \
--header 'Content-Type: multipart/form-data' \
--header 'Authorization: Bearer <ТОКЕН>' \
--form 'file=@/E:/Download/11111111_ACC.xlsx'


Пробовал путь указывать в там же формате как в примере на cURL, результата ноль(

Выручайте)
По теме из базы знаний
Вознаграждение за ответ
Показать полностью
Ответы
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
4. user856012 13 18.02.20 19:47 Сейчас в теме
(1)
ФайлДанных = "D:/11111111_ACC.xlsx";
А, может быть, надо так:
ФайлДанных = "D:\11111111_ACC.xlsx";
18. starik-2005 3039 07.11.23 12:13 Сейчас в теме
(1)
    ФайлОтправкиКонец.ЗаписатьСтроку(""); 
    ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary); 

    ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary + "--"); 


    ФайлОтправкиКонец.ЗаписатьСтроку(""); 
    ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary); 
    ФайлОтправкиКонец.ЗаписатьСтроку("");  // ++++++
    ФайлОтправкиКонец.ЗаписатьСтроку("--" + Boundary + "--"); 


И вроде как тут:
    Заголовки.Вставить("Content-Type","multipart/form-data; boundary=" + "--" +boundary + "");
"--" лишнее.
2. user649060 18.02.20 19:28 Сейчас в теме
Что у вас в первом варианте возвращает функция СоздатьСообщение_Файл?
3. dikd 17 18.02.20 19:40 Сейчас в теме
(2)
Сорри, забыл показать эту функцию

Вот:
Функция СоздатьСообщение_Файл(ИмяСообщения, ИмяФайла, ДвоичныеДанные)
    
    Поток = Новый ПотокВПамяти();
    ЗаписьДанных = Новый ЗаписьДанных(Поток);
	
	ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=" + ИмяСообщения + "; filename=" + ИмяФайла+"");
    ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/vnd.ms-excel"+ Символы.ПС );    
    ЗаписьДанных.Записать(ДвоичныеДанные);    
    ЗаписьДанных.Закрыть();
    
    Возврат Поток.ЗакрытьИПолучитьДвоичныеДанные();
    
КонецФункции
Показать
5. user649060 18.02.20 20:07 Сейчас в теме
(3) Пока из очевидного вижу только следующее:

В СоздатьСообщение_Файл у вас Content-Type выводится на одной строке с Content-Disposition.
Попробуйте использовать параметр РазделительСтрок.

Но это из мелочей..
6. user649060 18.02.20 20:22 Сейчас в теме
(3)

> ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=" + ИмяСообщения + "; filename=" + ИмяФайла+"");

Вот здесь надо ИмяСообщения и ИмяФайла дополнительно в кавычки на всякий случай завернуть.

Шаблон = "Content-Disposition: form-data; name=%1; filename=%2";
СтрокаКонтент = СтрШаблон(Шаблон,
"""" + ИмяСообщения + """",
"""" + ИмяФайла + """");



Да и попробуйте вообще путь из имени файла убрать. Оставить только [Имя].[Расширение]
7. user649060 18.02.20 20:26 Сейчас в теме
(3)
ЗаписьДанных.ЗаписатьСтроку("Content-Disposition: form-data; name=" + ИмяСообщения + "; filename=" + ИмяФайла+"");
ЗаписьДанных.ЗаписатьСтроку("Content-Type: application/vnd.ms-excel"+ Символы.ПС );
ЗаписьДанных.Записать(ДвоичныеДанные);
ЗаписьДанных.Закрыть();



Перед двоичными данными нужно записать еще пустую строку судя по стандарту.
https://tools.ietf.org/html/rfc7578
8. Romeo_1c_programmer 29 19.02.20 17:15 Сейчас в теме
Может нужно передавать данные в формате строки, закодированную по алгоритму base64?

ТекстXML = Base64Строка(БинДанные);
9. contrast 1 20.02.20 03:13 Сейчас в теме
Не удивлюсь, если сервер отдает ошибку 500 без Content-Length
vadim.semyonov.rzn; user649060; +2 Ответить
10. contrast 1 20.02.20 03:17 Сейчас в теме
Да и майм судя по названию топика должен быть application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
11. morin 58 20.02.20 14:38 Сейчас в теме
У меня есть пример работающего кода по аналогичной задаче, могу поделиться, если ещё актуально.
16. Revachol 08.06.22 13:06 Сейчас в теме
(11) Актуально)
По крайней мере вариант описанный в этой реализации, у меня не прокатывает( Файл передаётся битым.
12. uno-c 235 21.02.20 01:05 Сейчас в теме
В RFC 7578 в примерах имена полей везде в кавычках указаны:

Content-Disposition: form-data; name="user"
content-disposition: form-data; name="field1"
content-disposition: form-data; name="_charset_"

А вообще тут на инфостарте где-то библиотеку процедур и функций 1С для HTTP видел - там должно быть совсем просто, на готовом и проверенном. Вроде эта https://infostart.ru/public/709325/
13. miklems 19.03.21 16:07 Сейчас в теме
Есть решение? Тоже столкнулся с такой же проблемой.
14. mdzen 238 18.11.21 15:03 Сейчас в теме
И чем закончилось? Как-то решили? Техподдержка посоветовала "обратиться к поисковику".... Второй день бьюсь...
15. mdzen 238 20.11.21 10:58 Сейчас в теме
И так, для тех кто в теме. Техподдержка увы ничем не помогла.... Варианты перепробовал разные...
Все оказалось тривиально - В коде , который предложен в документации (это Вариант оправки №2, через Объединение файлов) , формирование тела письма происходит в кодировке UTF8, в то время как сервер не понимает ее, а принимает кодировку ANSI. Достаточно было изменить в функции ЗаписьТекста параметр КодировкаТекста UTF8 на ANSI и все работает!!!!

Здесь:
ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяФайлаОтправкиНачало, КодировкаТекста.UTF8);
 
//Заменить на --->

ФайлОтправкиНачало = Новый ЗаписьТекста(ИмяФайлаОтправкиНачало, КодировкаТекста.ANSI);


и здесь:
ФайлаОтправкиКонец = Новый ЗаписьТекста(ИмяФайлаОтправкиКонец, КодировкаТекста.UTF8);
 
//Заменить на --->

ФайлаОтправкиКонец = Новый ЗаписьТекста(ИмяФайлаОтправкиКонец, КодировкаТекста.ANSI);


В варианте 1 с потоком таже проблема - по умолчанию файл в кодировке UTF8. В параметрах функции ЗаписьДанных необходимо указать кодировку ANSI....
19. uno-c 235 10.11.23 22:14 Сейчас в теме
(15)
сервер не понимает ее, а принимает кодировку ANSI
Дело не в кодировке, тем более, что на первых 127 символах (в т.ч. английские буквы, цифры, символы над цифрами клавиатуры) кодировка UTF8 тождественна ANSI (ASCII). Дело в BOM (Byte Order Mark). Эска по-умолчанию дописываает BOM для кодировки UTF-8, надо специально указать, что BOM не нужен. Т.е. в конструкторе последний параметр <ЗаписатьBOM> надо Ложь поставить для кодировки UTF-8
Новый ЗаписьТекста(<Поток>, <Кодировка>, <РазделительСтрок>, <КонвертируемыйРазделительСтрок>, <ЗаписатьBOM>)

Меня как-то php-прогер попросил данные передавать в UTF-8, но сервер никак не мог их принять, пока не выяснили, что ему BOM мешает. Убрали BOM и заработало.
17. IvanVL 40 07.11.23 07:53 Сейчас в теме
Спасибо. Очень помогли все выкладки. Ситуация один в один как в шапке. Помог вариант 2. Понадобилось изменить минусы "--", убрал в самом начале начальные минусы, и убрал в конце конечные минусы.
Оставьте свое сообщение

Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот