7

我正在尝试使用 QNetworkAccessManager 将 http 多部分上传到专用服务器。

多部分由描述上传数据的 JSON 部分组成。

数据从串行 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 根本没有finished() 插槽。更重要的是,如果没有调用 QNetworkAccessManager::post(),则根本不会从我的自定义 IODevice 中读取数据,因此设备将无法发出这样的事件。(第 22 条?)

编辑2:

QNAM 似乎根本不适用于顺序设备。请参阅关于 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

4 回答 4

2

您需要大量重构代码,以便传递给的变量在post您发布的函数之外可用,然后您需要使用代码定义的新插槽来执行post内部实现。最后,您需要将它们connect(p_encDevice, SIGNAL(finished()), this, SLOT(yourSlot())粘合在一起。

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

于 2013-02-27T13:45:27.897 回答
1

与使用QHttpPartand相比,我手动创建 http post 数据的成功率更高QHttpMultiPart。我知道这可能不是你想听到的,而且有点乱,但它确实有效。在此示例中,我正在读取 a 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.";
?>

我希望其中一些代码可以帮助你。

于 2013-03-08T15:26:03.193 回答
1

我认为问题是 QNetworkAccessManager在上传(POST,PUT)数据时不支持分块传输编码。这意味着 QNAM 必须提前知道要上传的数据的长度,以便发送 Content-Length 标头。这意味着:

  1. 要么数据不是来自顺序设备,而是来自随机访问设备,这些设备将通过 正确报告它们的总大小size()
  2. 或者数据来自顺序设备,但是设备已经全部缓冲了(这就是关于的注释的意思finished()),并且会报告(通过bytesAvailable(),我想);
  3. 或者数据来自没有缓冲所有数据的顺序设备,这反过来意味着
    1. QNAM 读取并缓冲来自设备的所有数据(通过读取直到 EOF)
    2. 或者用户手动设置请求的 Content-Length 标头。

(关于最后两点,请参阅 QNetworkRequest::DoNotBufferUploadDataAttribute 的文档。)

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

(最后一点,你不应该使用 QScopedPointer。当智能指针超出范围时,这将删除 QNR,但你不想这样做。你想在 QNR 发出时删除它finished() )。

于 2013-03-09T12:46:55.683 回答
0

qt-project中的单独讨论和检查源代码来看,QNAM 似乎根本不适用于顺序。文档和代码都是错误的。

于 2013-03-13T11:16:01.167 回答