Отправка multipart/form-data запроса в Qt

в 9:54, , рубрики: c++, qt, Программирование, метки: ,

Иногда при разработке сетевого приложения возникает задача загрузки на сервер файла, и не просто так, а как части заполненной http формы. Это пример так называемого multipart/form-data запроса. Стандартные методы библиотеки Qt этого сделать не позволяют, поэтому приходится выкручиваться своими силами.

Общая информация

Итак, прежде всего необходимо понять, что же такого интересного содержит наш multipart/form-data запрос?
Если посмотреть на пример отсюда, типичный запрос представляет собой следующее:

POST http://www.site.ru/news.html HTTP/1.0rn
Host: www.site.rurn
Referer: http://www.site.ru/index.htmlrn
Cookie: income=1rn
Content-Type: multipart/form-data; boundary=1BEF0A57BE110FD467Arn
Content-Length: 209rn
rn
--1BEF0A57BE110FD467Arn
Content-Disposition: form-data; name="login"rn
rn
Petya Vasechkinrn
--1BEF0A57BE110FD467Arn
Content-Disposition: form-data; name="password"rn
rn
qqrn
--1BEF0A57BE110FD467A--rn


То, что нас особо интересует в заголовках — это boundary=1BEF0A57BE110FD467A и Content-Length: 209, после чего начинается тело запроса. Запрос состоит из нескольких частей, при этом разделителем будет считаться то, что написано как boundary, так же обязательно должна быть указана длина тела запроса — это поле Content-Length. Тело запроса — все начиная с первой строки --1BEF0A57BE110FD467A. В каждом разделе name — имя соответствующего поля формы, после двух переводов строк rnrn идет значение поля

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

--1BEF0A57BE110FD467Arn
Content-Disposition: form-data; name="news_file"; filename="news.txt"rn
Content-Type: application/octet-streamrn
Content-Transfer-Encoding: binaryrn
rn
А вот такая новость, которая лежит в файле news.txtrn


Здесь дополнительно задается имя файла — news.txt, а так же кодировка данных в поле Content-Transfer-Encoding. Есть несколько разных кодировок, в том числе представленная binary — незакодированные данные. С учетом возможностей Qt, очень удобно использовать кодировку base64. Если файл не просто какой-то там (application/octet-stream), а известного типа, то можно в поле Content-Type этот тип указать, например Content-Type: image/png.

Простой пример

Перейдем к практическому примеру формирования запроса. У нас есть:

//язык С++ и библиотека Qt
QNetworkAccessManager *manager;
//параметр 1 - какое-то поле, параметр 2 - файл
QByteArray param1Name="param1" ,param1Value="value1";
QByteArray param2Name="param2", param2FileName="news.txt",
    param2ContentType="text/plain",param2Data="А вот такая новость, которая лежит в файле news.txt";

Сформируем для начала тело запроса:

//задаем разделитель
QByteArray postData,boundary="1BEF0A57BE110FD467A";
//первый параметр
postData.append("--"+boundary+"rn");//разделитель
//имя параметра
postData.append("Content-Disposition: form-data; name="");
postData.append(param1Name);
postData.append(""rnrn");
//значение параметра
postData.append(param1Value);
postData.append("rn");

//параметр 2 - файл
postData.append("--"+boundary+"rn");//разделитель
//имя параметра
postData.append("Content-Disposition: form-data; name="");
postData.append(param2Name);
//имя файла
postData.append(""; filename="");
postData.append(param2FileName);
postData.append(""rn");
//тип содержимого файла
postData.append("Content-Type: "+param2ContentType+"rn");
//передаем в base64
postData.append("Content-Transfer-Encoding: base64rnrn");
//данные
postData.append(param2Data.toBase64());
postData.append("rn");
//"хвост" запроса
postData.append("--"+boundary+"--rn");

В переменной postData получаем готовое тело запроса — осталось только отослать и не забыть установить дополнительные заголовки запроса:

QNetworkRequest request(QUrl("http://example.com/submit.php"));
request.setHeader(QNetworkRequest::ContentTypeHeader,
	"multipart/form-data; boundary="+boundary);
request.setHeader(QNetworkRequest::ContentLengthHeader,
	QByteArray::number(postData.length()));
QNetworkReply *reply=manager->post(request,postData);

Ну а дальше — по накатанной дорожке, как для любых других запросов.

В итоге...

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

Полезная информация:
http://www.codenet.ru/webmast/php/HTTP-POST.php — описание начинки http запросов.

Автор: master1312


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js