Qt 之 QHttpPart和QHttpMultiPart (转载)

简述

HTTP 协议对 MIME 类型有详细描述,multipart/... 是单个消息头包含多个消息体的解决方案,multipart 类型对发送非文本类型非常有用。

multipart 子类型

首先,来看 QHttpMultiPart 中关于 multipart 子类型(subtype)的描述。

枚举 QHttpMultiPart::ContentType

RFC 2046 和其它地方描述的已知 multipart 子类型。

常量描述
QHttpMultiPart::MixedType0对应于 "multipart/mixed" 子类型,意味着 body 部位是相互独立的。如 RFC 2046 所述。
QHttpMultiPart::RelatedType1对应于 "multipart/related" 子类型,意味着 body 部位是相互关联的。如 RFC 2387 所述。
QHttpMultiPart::FormDataType2对应 "multipart/form-data" 子类型,意味着 body 部位包含表单元素。如 RFC 2388 所述。
QHttpMultiPart::AlternativeType3对应 "multipart/alternative" 子类型,意味着 body 部位是相同信息的替代表示。如 RFC 2046 所述。

QHttpPart

QHttpPart 类拥有一个 body 部位,用于 HTTP multipart MIME消息中(由 QHttpMultiPart 表示)。一个 QHttpPart 由一个 header 块和数据块组成,彼此之间存在两个连续换行。

一个 part 例子:

Content-Type: text/plain
Content-Disposition: form-data; name="text"
 
 
here goes the body

要设置 headers,使用 setHeader() 和 setRawHeader(),他们完全类似于 QNetworkRequest::setHeader() 和 QNetworkRequest::setRawHeader()。

对于读取小数据块,使用 setBody();如果是更大数据块,例如:图像,使用 setBodyDevice()。后一种方法由于内部没有复制数据,而是直接从设备读取,所以更节省内存。这意味着,当由 QNetworkAccessManager::post() 在网络上发送包含 body 部分的 multipart 消息时,设备必须打开并且可读。

构建一个小 body 的 QHttpPart,考虑下面的代码片段(将产生上述示例中显示的数据):

QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
textPart.setBody("here goes the body");

如果要从一个设备(例如:文件)读取,可以用这种方式:

QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\""));
imagePart.setRawHeader("Content-ID", "my@content.id"); // 添加任何你喜欢的 headers
QFile *file = new QFile("image.jpg");
file->open(QIODevice::ReadOnly);
imagePart.setBodyDevice(file);

注意: QHttpPart 在设置时不需要设备的所有权,所以应该在不需要的时候销毁该设备。比较好的办法是将 multipart 消息设置为设备的父对象,QHttpMultiPart 部分会有说明。

QHttpMultiPart

QHttpMultiPart 类似于一个 RFC 2046 所描述的 MIME multipart 消息,通过 HTTP 发送。

一个 multipart 消息包含任意数量的 body 部分(QHttpPart),由一个独特的 boundary 分割开来。QHttpMultiPart 的 boundary 由字符串 "boundary_.oOo._" 后面跟随机字符构造而成。并提供足够的唯一性,以确保它在每个部分内不重复。如果需要的话,boundary 仍然可以通过 setBoundary() 来设置。

例如,构造一个 multipart 消息,其包含一个文本部分,紧随其后的是一个图像部分:

QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
 
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
textPart.setBody("my text");
 
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\""));
QFile *file = new QFile("image.jpg");
file->open(QIODevice::ReadOnly);
imagePart.setBodyDevice(file);
file->setParent(multiPart); // 现在不能删除文件,所以用 multiPart 删除
 
multiPart->append(textPart);
multiPart->append(imagePart);
 
QUrl url("http://my.server.tld");
QNetworkRequest request(url);
 
QNetworkAccessManager manager;
QNetworkReply *reply = manager.post(request, multiPart);
multiPart->setParent(reply); // 用 reply 删除 multiPart
// 这里连接信号等

使用示例

比如multipart/form-data,其实就是浏览器用表单上传文件的方式。最常见的情景:发送邮件时添加附件,通常使用表单添加,也就是用 multipart/form-data 格式上传到服务器。

例如,上传一个本地的 png 格式图片:

这里写图片描述

可以看到请求内容的类型:

Content-Type: multipart/form-data; boundary="boundary_.oOo._MTA0NzE=MjcyNDY=ODk2Ng=="

其中表单类型为 multipart/form-data,boundary 是分隔符,和请求体中的分隔符内容一致。

由于上传附件不再使用原有的 HTTP 协议,所以请求体不再以 key=value 方式发送,而使用下述方式:

分隔符
字段内容1
分隔符
字段内容2

正如上图橙色区域显示方式一样。由于我们上传了一个图片,所以字段内容表示的是图片本身的字节(看上去像是乱码)。

注意:这里只是做了一个简单的演示,并没有把附件以表单的形式上传到任何服务器。如果要实际应用,可以调用一些第三方 API 进行尝试。

Qt 4.7版本中,确实包含了`QHttpPart`这个类,它主要用于构建HTTP表单数据,尤其是在与网络请求(如POST方法)一起工作时。`QHttpPart`用于分割和组织请求体的内容,比如文件、字符串或者其他自定义数据块。在构建像multipart/form-data这样的请求体时非常有用。 例如,在上述提到的POST Web API调用过程中,`QHttpMultiPart`会包含多个`QHttpPart`,每个`QHttpPart`代表表单的一部分,设置了名字、内容类型以及实际的数据体。 以下是使用`QHttpPart`的基本步骤: 1. 创建`QHttpMultiPart`对象,表示一个完整的表单: ```cpp QHttpMultiPart multiPart(QHttpMultiPart::FormDataType); ``` 2. 添加多个`QHttpPart`到表单中: ```cpp QHttpPart filePart; filePart.setHeader(QNetworkHeader::NameValuePair("file", "filename.txt")); filePart.setFileName(QStringLiteral("path_to_your_file")); filePart.setBody(QFile::readAll(QStringLiteral("path_to_your_file"))); multiPart.append(filePart); QHttpPart textPart; textPart.setHeader(QNetworkHeader::NameValuePair(QStringLiteral("key"), QStringLiteral("value"))); textPart.setValue(QStringLiteral("your_text_data")); multiPart.append(textPart); ``` 3. 将整个表单添加到`QNetworkRequest`中: ```cpp QNetworkRequest request(QUrl("http://example.com/api")); request.setRawHeader(QNetworkHeader::ContentType, multiPart.multipartBoundary()); request.setRawHeader(QNetworkHeader::ContentLength, multiPart.totalSize().toULongLong()); ``` 4. 进行网络请求: ```cpp QNetworkReply *reply = manager->post(request, multiPart); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值