QNetworkAccessManager:从串行QIODevice发布http multipart

时间:2013-02-27 10:24:40

标签: qt http post qt4 qnetworkaccessmanager

我正在尝试使用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);
}

4 个答案:

答案 0 :(得分:2)

你需要对你的代码进行很多重构,以便传递给post的变量在你发布的函数之外可用,然后你需要一个新的槽来定义代码来做实现中的post。最后,你需要connect(p_encDevice, SIGNAL(finished()), this, SLOT(yourSlot())将它们粘合在一起。

你大部分都在那里,你只需要重构它并添加一个新的插槽,你可以绑定到QIODevice::finished()信号。

答案 1 :(得分:1)

我手动创建http帖子数据比使用QHttpPartQHttpMultiPart更成功。我知道这可能不是你想听的,而且它有点乱,但绝对有效。在此示例中,我正在使用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标头。这意味着:

  1. 数据来自顺序设备,而是来自随机访问设备,这些设备会通过size()正确报告其总大小;
  2. 或者数据来自顺序设备,但设备已经缓存了所有内容(这是关于finished()的说明的含义),并会报告(通过bytesAvailable(),I想);
  3. 或者数据来自顺序设备,该设备没有缓冲所有数据,这反过来意味着
    1. 要么QNAM读取并缓冲来自设备的所有数据(通过读取直到EOF)
    2. 或用户手动设置请求的Content-Length标头。
  4. (关于最后两点,请参阅QNetworkRequest :: DoNotBufferUploadDataAttribute的文档。)

    所以,QHttpMultiPart会以某种方式分享这些限制,并且它很可能在案例3中窒息。假设你不可能在内存中缓冲来自“编码器”QIODevice的所有数据,你是否有可能知道它的大小预先编码数据并在QHttpPart上设置内容长度?

    (作为最后一点,您不应该使用QScopedPointer。当智能指针超出范围时会删除QNR,但您不​​想这样做。您希望在QNR发出时删除它成品())。

答案 3 :(得分:0)

通过qt-project中的单独讨论并通过检查源代码,似乎QNAM根本无法使用顺序。文档和代码都是错误的。