Иногда при разработке сетевого приложения возникает задача загрузки на сервер файла, и не просто так, а как части заполненной 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