Перейти к содержимому

Как отправить файл post запросом

  • автор:

Как передать по http одним POST запросом файл (двоичные данные) и JSON?

Файл передается и принимается на сервере нормально, а массив нет. Когда смотрю запрос в DevTools вижу это:

 Form Data: file: (binary) text: (unable to decode value) 

Вот запрос:

fetch('/api/newPost', < method: "POST", body: formData >) 

Отслеживать
задан 18 окт 2019 в 20:10
Dmitry Filippov Dmitry Filippov
623 5 5 серебряных знаков 24 24 бронзовых знака

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Перед передачей преобразуйте массив в json-строку. Передавать форму надо как multipart/form-data , а потом на сервере обрабатывать каждое поле отдельно.

let formData = new FormData(); formData.append('file', blob); formData.append('text', JSON.stringify(['dima', 'sasha'])); fetch('/api/newPost', < method: "POST", headers: < "Content-Type": "multipart/form-data" >, body: formData >) 

Отслеживать
ответ дан 18 окт 2019 в 20:46
Dmitry Kozlov Dmitry Kozlov
6,708 2 2 золотых знака 15 15 серебряных знаков 30 30 бронзовых знаков

  • javascript
  • node.js
  • formdata
    Важное на Мете
Похожие

Подписаться на ленту

Лента вопроса

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.1.3.2953

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

POST запрос, составное содержимое (multipart/form-data)

POST multipart/form-data

В жизни любого программиста попадаются задачки, которые человека цепляют. Вот не нравится стандартный метод решения и все! А порой бывает, что стандартные решения не подходят по какой-то причине. Некоторые люди обходят такие задачи стороной, другие же любят решать их. Можно даже сказать сами их находят. Одна из таких задач отсылка файла или несколько файлов методом POST.

Некоторые наверное скажут, эта задача совсем не задача. Ведь есть замечательная библиотека CURL, которая довольно простая и решает эту задачу легко! Но не спешите. Да, CURL мощная библиотека, да она загружает файлы, но… Как Вы знаете у нее есть маленькая особенность — файл должен быть размещен на жестком диске!

А теперь давайте представим себе такую ситуацию, Вы генерируете динамически файл или же он уже находится в памяти и нужно его отправить методом POST на удаленный Web сервер. Что же тогда получается? Перед его отправкой нужно его сохранить? Да именно так и поступило бы 90% программистов. Зачем искать лишние проблемы, если решение лежит на поверхности? Но мы же с Вами не из этих 90%! Мы же лучше, мы же можем решить любую задачку. Зачем нам лишнее действие? Во-первых, оно задействует не быструю файловую систему жесткого диска. Во-вторых, у нас может и не быть доступа к файловой системе или же там выделено слишком мало места.

Как же нам тогда решить эту задачку? Для этого надо взглянуть как собственно передаются данные методом POST. Единственный вариант решения — это передача файла составным запросом с помощью multipart/form-data. Этот метод хорошо описан в RFC7578. Давайте взглянем как будет выглядеть тело POST запроса multipart/form-data:

POST /form.html HTTP/1.1 Host: server.com Referer: http://server.com/form.html User-Agent: Mozilla Content-Type: multipart/form-data; boundary=-------------573cf973d5228 Content-Length: 288 Connection: keep-alive Keep-Alive: 300 (пустая строка) (отсутствующая преамбула) ---------------573cf973d5228 Content-Disposition: form-data; name="field" text ---------------573cf973d5228 Content-Disposition: form-data; name="file"; filename="sample.txt" Content-Type: text/plain Content file ---------------573cf973d5228--

Наше тело состоит из двух частей, в первой части мы передаем значение поля формы name=«field» равное: text. Во второй части мы передаем поле name=«file» с содержимым файла filename=«sample.txt»: Content file. В заголовке мы указываем формат содержимого POST запроса — Content-Type: multipart/form-data, строку разделитель составных частей: boundary=————-573cf973d5228 и длину сообщения — Content-Length: 288.

Осталось, собственно, написать программу реализующий этот метод. Так как мы люди умные и не пишем по сто раз одно и тоже в разных проектах, то оформим все в виде класса реализующий этот метод. Плюс к этому, расширим его для разных вариантов отправки как файлов, так и простых элементов формы. А что бы отличить среди массива POST данных, наличие файла, создадим отдельный файл — контейнер с содержимым файла и его данных (имя и расширение). Таким образом он будет выглядеть следующим образом:

 
class oFile < private $name; private $mime; private $content; public function __construct($name, $mime=null, $content=null) < // Проверяем, если $content=null, значит в переменной $name - путь к файлу if(is_null($content)) < // Получаем информацию по файлу (путь, имя и расширение файла) $info = pathinfo($name); // проверяем содержится ли в строке имя файла и можно ли прочитать файл if(!empty($info['basename']) && is_readable($name)) < $this->name = $info['basename']; // Определяем MIME тип файла $this->mime = mime_content_type($name); // Загружаем файл $content = file_get_contents($name); // Проверяем успешно ли был загружен файл if($content!==false) $this->content = $content; else throw new Exception('Don`t get content - "'.$name.'"'); > else throw new Exception('Error param'); > else < // сохраняем имя файла $this->name = $name; // Если не был передан тип MIME пытаемся сами его определить if(is_null($mime)) $mime = mime_content_type($name); // Сохраняем тип MIME файла $this->mime = $mime; // Сохраняем в свойстве класса содержимое файла $this->content = $content; >; > // Метод возвращает имя файла public function Name() < return $this->name; > // Метод возвращает тип MIME public function Mime() < return $this->mime; > // Метод возвращает содержимое файла public function Content() < return $this->content; > >;

Теперь собственно сам класс по формированию тела multipart/form-data для POST запроса:

 
class BodyPost < //Метод формирования части составного запроса public static function PartPost($name, $val) < $body = 'Content-Disposition: form-data; name="' . $name . '"'; // Проверяем передан ли класс oFile if($val instanceof oFile) < // Извлекаем имя файла $file = $val->Name(); // Извлекаем MIME тип файла $mime = $val->Mime(); // Извлекаем содержимое файла $cont = $val->Content(); $body .= '; filename="' . $file . '"' . "\r\n"; $body .= 'Content-Type: ' . $mime ."\r\n\r\n"; $body .= $cont."\r\n"; > else $body .= "\r\n\r\n".urlencode($val)."\r\n"; return $body; > // Метод формирующий тело POST запроса из переданного массива public static function Get(array $post, $delimiter='-------------0123456789') < if(is_array($post) && !empty($post)) < $bool = false; // Проверяем есть ли среди элементов массива файл foreach($post as $val) if($val instanceof oFile) ; if($bool) < $ret = ''; // Формируем из каждого элемента массива, составное тело POST запроса foreach($post as $name=>$val) $ret .= '--' . $delimiter. "\r\n". self::PartPost($name, $val); $ret .= "--" . $delimiter . "--\r\n"; > else $ret = http_build_query($post); > else throw new \Exception('Error input param!'); return $ret; > >;

Данный класс состоит из нескольких методов. Метод — PartPost формирует отдельные части составного запроса, а метод — Get объединяет эти части и формирует тело POST запроса в формате — multipart/form-data.

Теперь у нас есть универсальный класс для отправки тела POST запроса. Осталось написать программу использующую данный класс для отправки файлов на удаленный Web сервер. Воспользуемся библиотекой CURL:

// Подключаем класс-контейнер содержимого файла include "ofile.class.php"; // Подключаем класс для формирования тела POST запроса include "bodypost.class.php"; // Генерируем уникальную строку для разделения частей POST запроса $delimiter = '-------------'.uniqid(); // Формируем объект oFile содержащий файл $file = new oFile('sample.txt', 'text/plain', 'Content file'); // Формируем тело POST запроса $post = BodyPost::Get(array('field'=>'text', 'file'=>$file), $delimiter); // Инициализируем CURL $ch = curl_init(); // Указываем на какой ресурс передаем файл curl_setopt($ch, CURLOPT_URL, 'http://server/upload/'); // Указываем, что будет осуществляться POST запрос curl_setopt($ch, CURLOPT_POST, 1); // Передаем тело POST запроса curl_setopt($ch, CURLOPT_POSTFIELDS, $post); /* Указываем дополнительные данные для заголовка: Content-Type - тип содержимого, boundary - разделитель и Content-Length - длина тела сообщения */ curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data; boundary=' . $delimiter, 'Content-Length: ' . strlen($post))); // Отправляем POST запрос на удаленный Web сервер curl_exec($ch); 

Если CURL не подходит, то данную библиотеку можно применить и для отправки через сокеты. Ну и собственно ссылки на источники:

  • сайт документации php.net
  • статья CURL: POST запрос, составное содержимое
  • википендия: multipart/form-data
  • RFC7578

Отправить файл в POST-запросе

Чтобы отправить файл в POST-запросе, используют функцию curl_file_create() , которая стала доступна с версии PHP 5.5.0.

// переменные для POST-запроса $post = [ 'var_name' => 'value', 'userfile' => curl_file_create('path/file.jpg'), ]; // отправка POST-запроса $ch = curl_init('http://example.com/'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $data = curl_exec($ch);

Обратится к данному файлу можно через переменную $_FILES .

// загрузка файла move_uploaded_file($_FILES['userfile']['tmp_name'], 'upload/new-file.jpg');

Обновлено: 01 ноября 2020

Комментарии

Авторизуйтесь, чтобы добавлять комментарии

Как отправить файл POST запросом через HttpURLConnection?

Разобрался в предыдущем запросе как отправлять POST запрос с параметрами на PHP сервер с помощью HttpURLConnection в Android. Теперь столкнулся с проблемой отправки файла (изображение или видеофайл) с текстовыми параметрами (ид и логин пользователя).

После диалога выбора фото я имею URI и Bitmap тип. Выбор видео пока не смог реализовать.

Знаю что нужно изменить тип запроса на мульти тип, но не могу разобраться как переделать свой код:

class SendLoginData extends AsyncTask  < String resultString = null; @Override protected void onPreExecute() < super.onPreExecute(); >@Override protected Void doInBackground(Void. params) < try < String myURL = "http://site.ru/"; String parammetrs = "param1=1&param2=XXX"; byte[] data = null; InputStream is = null; try < URL url = new URL(myURL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestProperty("Content-Length", "" + Integer.toString(parammetrs.getBytes().length)); OutputStream os = conn.getOutputStream(); data = parammetrs.getBytes("UTF-8"); os.write(data); data = null; conn.connect(); int responseCode= conn.getResponseCode(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (responseCode == 200) < is = conn.getInputStream(); byte[] buffer = new byte[8192]; // Такого вот размера буфер // Далее, например, вот так читаем ответ int bytesRead; while ((bytesRead = is.read(buffer)) != -1) < baos.write(buffer, 0, bytesRead); >data = baos.toByteArray(); resultString = new String(data, "UTF-8"); > else < >> catch (MalformedURLException e) < //resultString = "MalformedURLException:" + e.getMessage(); >catch (IOException e) < //resultString = "IOException:" + e.getMessage(); >catch (Exception e) < //resultString = "Exception:" + e.getMessage(); >> catch (Exception e) < e.printStackTrace(); >return null; > @Override protected void onPostExecute(Void result) < super.onPostExecute(result); if(resultString != null) < Toast toast = Toast.makeText(getApplicationContext(), resultString, Toast.LENGTH_SHORT); toast.show(); >> >

Надеюсь на вашу помощь. Перечитал кучу информации в интернете, но так и не разобрался.
Спасибо.

  • Вопрос задан более трёх лет назад
  • 7362 просмотра

3 комментария

Оценить 3 комментария

Wolfak @Wolfak Автор вопроса
Moxa: слишком сложный код для меня, я его не понимаю. Мне нужно изменить мой. Но спасибо.
Wolfak: просто так multipart не отправить, поддержки из коробки нет.
возьмите готовую либу
Решения вопроса 1

https://github.com/kevinsawicki/http-request — рекомендую библиотеку, использует HttpURLConnection, просто и удобно.

В вашем случае будет как то так:

HttpRequest request = HttpRequest.post("http://site.ru/"); request.part("param1", "1"); request.part("param2", "xxx"); request.part("file", new File("/test.txt")); int status = request.code(); if(status == 200)

Ответ написан более трёх лет назад
Нравится 1 24 комментария
Wolfak @Wolfak Автор вопроса
А не подскажите как его установить, чтобы опробовать? У меня проблемы с английским( Спасибо.

1. Подключить через maven:
com.github.kevinsawickihttp-request6.0

2. Скачать файл-класс HttpRequest.java https://raw.githubusercontent.com/kevinsawicki/htt. и закинуть его в папку с проектом \app\src\main\java\com\github\kevinsawicki\http (папки после java создать)

Теги из первого варианта съелись, код есть на https://github.com/kevinsawicki/http-request под заголовком Usage

Wolfak @Wolfak Автор вопроса

coden55: а куда и что копировать из github? т.е. куда сохранять папку или файлы библиотеки не подскажите? Использую Android studio

Там один файл. Прочитай внимательно коммент, там и ссылка на файл есть и куда закинуть
Wolfak @Wolfak Автор вопроса

coden55: вроде все подключил правильно, сделал вот такую передачу bitmap в файл и потом отправку файла, но файл не отправляется. Если убрать все связанное с файлом, а оставить только параметры то все работает и возвращает ответ отлично.
File imageFile = new File(«/», «preload.png»);
OutputStream FileOutputStream;
try FileOutputStream = new FileOutputStream(imageFile);
thumbnailBitmap.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream);
FileOutputStream.flush();
FileOutputStream.close();
> catch (Exception e)
Если отправляете картинку, не забудьте указать правильный content-type

Файл по факту создается? Какая ошибка выводится?
В среде android файлы читаются/пишутся иначе чем в простом java startandroid.ru/ru/uroki/vse-uroki-spiskom/138-uro.

в request.part можно скормить объект класса File или FileInputStream

Wolfak @Wolfak Автор вопроса

coden55: указал content-type таким способом для отправки файлов:
request.accept(«multipart/form-data»);
Надеюсь правильно.

Wolfak @Wolfak Автор вопроса

coden55: С отправкой разобрался. Теперь не могу понять как сохранить сам файл на сервере с помощью PHP. $_POST[«file»] ничего не содержит. php://input тоже.

Ну тут и английский не нужно знать, что то ты совсем ленивый:)
php.net/manual/ru/features.file-upload.post-method.php

Wolfak @Wolfak Автор вопроса
coden55: пробовал) $_FILES тоже передаётся пустым, но файл 100% грузится.
Wolfak @Wolfak Автор вопроса

coden55: и когда пытаюсь прочесть $_FILES всегда пишет ошибку java.net.SocketException: sendto failed: EPIPE (Broken pipe)

Смотри конфигурацию php php.net/manual/ru/ini.core.php#ini.file-uploads
Возможно загрузки файлов запрещены и пхп их удаляет

Или по объему файла не проходит
Wolfak @Wolfak Автор вопроса

coden55: с конфигурацией все в порядке, так как через браузерную версию сайта и клиент на Windows Phone все отлично загружает)

А как понял что файл прогружает?
Wolfak @Wolfak Автор вопроса

coden55: Грузиться стало дольше чем обычно раза в 4-5 ну и перестали писаться ошибки в логах о проблеме выбора файла и т.д. Единственное что скрипт не получает файл или я не правильно его пытаюсь получить (такое уже было с клиентом под Windows Phone, тоже долго не мог определить как передается файл пост запросом, оказалось что через php://input.

Wolfak @Wolfak Автор вопроса

coden55: определил через if($_POST[«file»]) что $_POST[«file»] получается не пустым (в отличии от $_FILES). Теперь ищу как его сохранить в файл.

Wolfak @Wolfak Автор вопроса

coden55: разобрался. Сохранение файла работает так:
$fileDirectURL = «/home/***/www/site.ru/tmp/123.png»;
file_put_contents($fileDirectURL, $_POST[«file»]);
Вот только пришлось сделать сжатие изображения перед отправкой, так как большие файлы таким способом не отправляются:
// Подготовка файла
final int maxSize = 1280;
final int maxSizeH = 1024;
int outWidth;
int outHeight;
int inWidth = thumbnailBitmap.getWidth();
int inHeight = thumbnailBitmap.getHeight();
if(inWidth > inHeight) outWidth = maxSize;
outHeight = (inHeight * maxSize) / inWidth;
> else outHeight = maxSizeH;
outWidth = (inWidth * maxSizeH) / inHeight;
>

Bitmap ResthumbnailBitmap = Bitmap.createScaledBitmap(thumbnailBitmap, outWidth, outHeight, false);

String FileDir = getFilesDir() + File.separator + «preload.png»;
File imageFile = new File(FileDir);
imageFile.createNewFile();
OutputStream FileOutputStream;
try FileOutputStream = new FileOutputStream(imageFile);
ResthumbnailBitmap.compress(Bitmap.CompressFormat.JPEG, 100, FileOutputStream);
FileOutputStream.flush();
FileOutputStream.close();
> catch (Exception e) Log.e(getClass().getSimpleName(), «Error writing bitmap!», e);
>

request.part(«file», new FileInputStream(FileDir));

if(imageFile.exists()) boolean deleted = imageFile.delete();
>
// Подготовка файла

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *