我正在尝试使用QNetworkAccessManager将http多部分上传到专用服务器。
multipart由描述正在上传的数据的JSON部分组成。
从串行QIODevice读取数据,QIODevice加密数据。
这是创建多部分请求的代码:
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart metaPart;
metaPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
metaPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"metadata\""));
metaPart.setBody(meta.toJson());
multiPart->append(metaPart);
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(fileFormat));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\""));
filePart.setBodyDevice(p_encDevice);
p_encDevice->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(filePart);
QNetworkAccessManager netMgr;
QScopedPointer<QNetworkReply> reply( netMgr.post(request, multiPart) );
multiPart->setParent(reply.data()); // delete the multiPart with the reply
如果p_encDevice是QFile的一个实例,该文件上传就好了。
如果使用专用加密QIODevice(串行设备),则从我的自定义设备读取所有数据。但是QNetworkAccessManager :: post()没有完成(挂起)。
我在QHttpPart的文档中读到:
如果设备是顺序的(例如套接字,而不是文件), 应该在设备拥有后调用QNetworkAccessManager :: post() 发射完成()。
不幸的是我不知道那是怎么回事。
请告知。
修改
QIODevice根本没有完成()插槽。更重要的是,如果没有调用QNetworkAccessManager :: post(),那么从我的自定义IODevice读取根本不会发生,因此设备将无法发出这样的事件。 (赶上22?)
编辑2:
似乎QNAM根本无法使用顺序设备。请参阅discussion on qt-project。
编辑3:
我设法“愚弄”QNAM让它认为它是从非顺序设备读取,但搜索和重置功能阻止搜索。这将有效,直到QNAM真正尝试寻求。
bool AesDevice::isSequential() const
{
return false;
}
bool AesDevice::reset()
{
if (this->pos() != 0) {
return false;
}
return QIODevice::reset();
}
bool AesDevice::seek(qint64 pos)
{
if (this->pos() != pos) {
return false;
}
return QIODevice::seek(pos);
}
答案 0 :(得分:2)
你需要对你的代码进行很多重构,以便传递给post
的变量在你发布的函数之外可用,然后你需要一个新的槽来定义代码来做实现中的post
。最后,你需要connect(p_encDevice, SIGNAL(finished()), this, SLOT(yourSlot())
将它们粘合在一起。
你大部分都在那里,你只需要重构它并添加一个新的插槽,你可以绑定到QIODevice::finished()
信号。
答案 1 :(得分:1)
我手动创建http帖子数据比使用QHttpPart
和QHttpMultiPart
更成功。我知道这可能不是你想听的,而且它有点乱,但绝对有效。在此示例中,我正在使用QFile
进行阅读,但您可以在任何readAll()
上致电QIODevice
。值得注意的是,QIODevice::size()
将帮助您检查是否已读取所有数据。
QByteArray postData;
QFile *file=new QFile("/tmp/image.jpg");
if(!(file->open(QIODevice::ReadOnly))){
qDebug() << "Could not open file for reading: "<< file->fileName();
return;
}
//create a header that the server can recognize
postData.insert(0,"--AaB03x\r\nContent-Disposition: form-data; name=\"attachment\"; filename=\"image.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n");
postData.append(file->readAll());
postData.append("\r\n--AaB03x--\r\n");
//here you can add additional parameters that your server may need to parse the data at the end of the url
QString check(QString(POST_URL)+"?fn="+fn+"&md="+md);
QNetworkRequest req(QUrl(check.toLocal8Bit()));
req.setHeader(QNetworkRequest::ContentTypeHeader,"multipart/form-data; boundary=AaB03x");
QVariant l=postData.length();
req.setHeader(QNetworkRequest::ContentLengthHeader,l.toString());
file->close();
//free up memory
delete(file);
//post the data
reply=manager->post(req,postData);
//connect the reply object so we can track the progress of the upload
connect(reply,SIGNAL(uploadProgress(qint64,qint64)),this,SLOT(updateProgress(qint64,qint64)));
然后服务器可以访问这样的数据:
<?php
$filename=$_REQUEST['fn'];
$makedir=$_REQUEST['md'];
if($_FILES["attachment"]["type"]=="image/jpeg"){
if(!move_uploaded_file($_FILES["attachment"]["tmp_name"], "/directory/" . $filename)){
echo "File Error";
error_log("Uploaded File Error");
exit();
};
}else{
print("no file");
error_log("No File");
exit();
}
echo "Success.";
?>
我希望这段代码可以帮到你。
答案 2 :(得分:1)
我认为问题在于,QNetworkAccessManager在上传(POST,PUT)数据时不支持chunked transfer encoding。这意味着QNAM必须事先知道要上传的数据的长度,以便发送Content-Length标头。这意味着:
size()
正确报告其总大小; finished()
的说明的含义),并会报告(通过bytesAvailable()
,I想); (关于最后两点,请参阅QNetworkRequest :: DoNotBufferUploadDataAttribute的文档。)
所以,QHttpMultiPart会以某种方式分享这些限制,并且它很可能在案例3中窒息。假设你不可能在内存中缓冲来自“编码器”QIODevice的所有数据,你是否有可能知道它的大小预先编码数据并在QHttpPart上设置内容长度?
(作为最后一点,您不应该使用QScopedPointer。当智能指针超出范围时会删除QNR,但您不想这样做。您希望在QNR发出时删除它成品())。
答案 3 :(得分:0)
通过qt-project中的单独讨论并通过检查源代码,似乎QNAM根本无法使用顺序。文档和代码都是错误的。