Pull to refresh

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

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

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


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

POST http://www.site.ru/news.html HTTP/1.0\r\n
Host: www.site.ru\r\n
Referer: http://www.site.ru/index.html\r\n
Cookie: income=1\r\n
Content-Type: multipart/form-data; boundary=1BEF0A57BE110FD467A\r\n
Content-Length: 209\r\n
\r\n
--1BEF0A57BE110FD467A\r\n
Content-Disposition: form-data; name="login"\r\n
\r\n
Petya Vasechkin\r\n
--1BEF0A57BE110FD467A\r\n
Content-Disposition: form-data; name="password"\r\n
\r\n
qq\r\n
--1BEF0A57BE110FD467A--\r\n

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

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

--1BEF0A57BE110FD467A\r\n
Content-Disposition: form-data; name="news_file"; filename="news.txt"\r\n
Content-Type: application/octet-stream\r\n
Content-Transfer-Encoding: binary\r\n
\r\n
А вот такая новость, которая лежит в файле news.txt\r\n

Здесь дополнительно задается имя файла — 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+"\r\n");//разделитель
//имя параметра
postData.append("Content-Disposition: form-data; name=\"");
postData.append(param1Name);
postData.append("\"\r\n\r\n");
//значение параметра
postData.append(param1Value);
postData.append("\r\n");

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

В переменной 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 запросов.
http://www.ietf.org/rfc/rfc2388.txt — Стандарт «Returning Values from Forms: multipart/form-data»
Tags:
Hubs:
+3
Comments20

Articles