网页的form表单中,如果存在上传文件的表单,则需要将form标签设置enctype="multipart/form-data"属性,意思是将Content-Type设置成multipart/form-data,multipart 类型对发送非文本类型非常有用。Qt中对应为QHttpMultiPart、QHttpPart,QHttpMultiPart 类似于一个 RFC 2046 所描述的 MIME multipart 消息,通过 HTTP 发送,常用于传输较大的文件数据。
Constant 常数 | Value 价值 | Description 描述 |
---|---|---|
QHttpMultiPart::MixedType |
0 |
对应于"multipart/mixed"子类型,意味着主体部件彼此独立,如RFC 2046中所述。 |
QHttpMultiPart::RelatedType |
1 |
对应于"multipart/related"子类型,意味着主体部件彼此相关,如RFC 2387中所述。 |
QHttpMultiPart::FormDataType |
2 |
对应于“multipart/form-data”子类型,这意味着主体部分包含表单元素,如RFC 2388中所述。 |
QHttpMultiPart::AlternativeType |
3 |
对应于"multipart/alternative"子类型,这意味着主体部分是相同信息的可选表示,如RFC 2046中所述。 |
下面我们看下微信小程序云服务器中关于上传文件的规定:
用户获取到返回数据后,需拼装一个 HTTP POST 请求,其中 url 为返回包的 url 字段,Body 部分格式为 multipart/form-data,具体内容如下:
key | value | 说明 |
---|---|---|
key | this/is/a/example/file.path | 请求包中的 path 字段 |
Signature | q-sign-algorithm=sha1&q-ak=AKID9... | 返回数据的 authorization 字段 |
x-cos-security-token | Cukha70zkXIBqkh1Oh... | 返回数据的 token 字段 |
x-cos-meta-fileid | HDze32/qZENCwWi5... | 返回数据的 cos_file_id 字段 |
file | 文件内容 | 文件的二进制内容 |
其中HttpPart部分的key、Signature、x-cos-security-token、x-cos-meta-fileid均由请求地址返回数据获得。:
POST https://api.weixin.qq.com/tcb/uploadfile?access_token=ACCESS_TOKEN
知道了上传文件的方法,现在我们开始撸起袖子加油干吧!
文档中说我们不需要指定boundary,它也是会自动给你添加boundary的,前提是必须保持MIME中的boundary和请求头里的boundary值一致,因此需要添加请求头中的boundary,否则传不了,如果自定义boundary,也需要和请求头中的保持一致!(官方文档中的说法存在一定的问题)
QNetworkRequest mulRequest;
mulRequest.setHeader(QNetworkRequest::ContentTypeHeader,"multipart/form-data;boundary=boundary_.oOo._");
上传的表单中的Body部分,使用QHttpPart对每个需要上传的字段进行设置,QHttpPart 类拥有一个 body 部位,用于 HTTP multipart MIME消息中(由 QHttpMultiPart 表示)。一个 QHttpPart 由一个 header 块和数据块组成,彼此之间存在两个连续换行。要设置 headers,使用 setHeader() 和 setRawHeader(),对于读取小数据块,使用 setBody();如果是更大数据块,例如:图像,使用 setBodyDevice()。下面看看上传文件名为“3.png”的图片至小程序云服务器的代码段:
void JsonDataModel::sendFileData(QString path,QString url,QString authorization,QString token,QString cos_file_id){//此处为获取到的body数据,待上传用
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QNetworkRequest mulRequest;
mulRequest.setHeader(QNetworkRequest::ContentTypeHeader,"multipart/form-data;boundary=boundary_.oOo._");//此处boundary不能省!需要和QHttpMultiPart中的一致
QString fileName = QFileDialog::getOpenFileName(0,
tr("选择图片"),QDir::currentPath(), tr("Image Files (*.png *.jpg *.bmp)"));//选择3.png的路径
QFile *file = new QFile(fileName,multiPart);//开辟新的储存空间给文件,当释放multiPart空间时同时释放文件空间
file->open(QIODevice::ReadOnly);
QHttpPart keyPart;
keyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"key\""));
keyPart.setBody(path.toUtf8());
QHttpPart authorizationPart;
authorizationPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"Signature\""));
authorizationPart.setBody(authorization.toUtf8());
QHttpPart tokenPart;
tokenPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"x-cos-security-token\""));
tokenPart.setBody(token.toUtf8());
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"x-cos-meta-fileid\""));
filePart.setBody(cos_file_id.toUtf8());
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data;name=\"file\""));
// imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data;name=\"file\";filename=\"3.png\"")); 部分服务器可能需要添加filename字段,小程序云服务器不需要此字段
imagePart.setBodyDevice(file);
multiPart->append(keyPart);
multiPart->append(authorizationPart);
multiPart->append(tokenPart);
multiPart->append(filePart);
multiPart->append(imagePart);
QUrl u(url);
mulRequest.setUrl(u);
QNetworkReply *r = manager->post(mulRequest, multiPart);
connect(r,SIGNAL(finished()),this,SLOT(slotSendFileData()));
multiPart->setParent(r); // delete the multiPart with the reply防止内存泄漏,在槽函数中释放
}
上传后图片在小程序云开发控制台中查看: