使用QNetworkAccessManager的post()方法上传文件

时间:2010-05-10 20:35:54

标签: windows http qt post qnetworkaccessmanager

我在使用Qt应用程序时遇到了一些麻烦;特别是QNetworkAccessManager类。我正在尝试使用QNetworkAccessManager的post()方法执行二进制文件的简单HTTP上载。文档声明我可以将指向QIODevice的指针发送到post(),并且该类将传输在QIODevice中找到的数据。这告诉我,我应该能够给post()一个指向QFile的指针。例如:

QFile compressedFile("temp");  
compressedFile.open(QIODevice::ReadOnly);  
netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), &compressedFile);  

在我开发这个系统的Windows系统上似乎发生的事情是我的Qt应用程序从QFile推送数据,但后来没有完成请求;它似乎坐在那里等待从文件中显示更多数据。在手动终止应用程序之前,post请求不会“关闭”,此时整个文件会显示在我的服务器端。

从一些调试和研究中,我认为这种情况正在发生,因为当你到达文件末尾时,QFile的read()操作不会返回-1。我认为QNetworkAccessManager试图从QIODevice读取,直到它从read()获得-1,此时它假定没有更多数据并关闭请求。如果它继续从read()获得零返回码,QNetworkAccessManager假定可能有更多数据到来,因此它一直在等待该假设数据。

我已经确认了一些测试代码,在你读完文件末尾后,QFile的read()操作只返回零。这似乎与QNetworkAccessManager的post()方法期望QIODevice的行为方式不兼容。我的问题是:

  1. 这是否与QFile在Windows下的工作方式有某种限制?
  2. 还有其他方法我应该使用QFile或QNetworkAccessManager通过post()推送文件吗?
  3. 这根本不起作用,我还需要找其他方法来上传我的文件吗?
  4. 任何建议或提示都将不胜感激。

    更新:事实证明我遇到了两个不同的问题:一个在客户端,另一个在服务器端。在客户端,我必须确保我的QFile对象在网络事务期间保持不变。 QNetworkAccessManager的post()方法立即返回,但实际上并未立即完成。您需要将一个槽附加到QNetworkAccessManager的finished()信号,以确定POST何时实际完成。在我的情况下,很容易将QFile保持在或多或少永久,但我还在finish()信号上附加了一个插槽,以便检查来自服务器的错误响应。

    我将信号附加到插槽中:

    connect(&netManager, SIGNAL(finished(QNetworkReply*) ), this, SLOT(postFinished(QNetworkReply*) ) );  
    

    到了发送我的文件的时候,我写了这样的帖子代码(请注意,compressedFile是我班级的成员,因此在此代码之后不会超出范围):

    compressedFile.open(QIODevice::ReadOnly);  
    netManager.post(QNetworkRequest(QUrl(httpDestination.getCString() ) ), &compressedFile);  
    

    来自QNetworkAccessManager的完成(QNetworkReply *)信号触发了我的postFinished(QNetworkReply *)方法。发生这种情况时,我可以安全地关闭compressedFile并删除co​​mpressedFile表示的数据文件。出于调试目的,我还添加了一些printf()语句来确认事务已完成:

    void CL_QtLogCompressor::postFinished(QNetworkReply* reply)  
    {  
        QByteArray response = reply->readAll();  
        printf("response: %s\n", response.data() );  
        printf("reply error %d\n", reply->error() );  
        reply->deleteLater();  
        compressedFile.close();  
        compressedFile.remove();  
    }  
    

    由于compressedFile没有立即关闭并且没有超出范围,因此QNetworkAccessManager可以花费尽可能多的时间来传输我的文件。最终事务完成,我的postFinished()方法被调用。

    我的另一个问题(这也导致我看到事务从未完成的行为)是我的Web服务器的Python代码没有正确地部署POST,但这超出了我原来的Qt问题的范围。

2 个答案:

答案 0 :(得分:8)

您正在堆栈上创建compressedFile,并将指针传递给您的QNetworkRequest(最终是您的QNetworkAccessManager)。一旦离开您所在的方法,compressedFile就会超出范围。我很惊讶它没有撞到你,虽然行为是未定义的。

您需要在堆上创建QFile

QFile *compressedFile = new QFile("temp"); 

当然,您需要跟踪它,然后在帖子完成后delete跟踪它,或者将其设置为QNetworkReply的子项,以便在回复获得后将其销毁后来被摧毁:

QFile *compressedFile = new QFile("temp"); 
compressedFile->open(QIODevice::ReadOnly);

QNetworkReply *reply = netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), compressedFile); 
compressedFile->setParent(reply);

答案 1 :(得分:3)

您还可以使用信号/插槽

安排自动删除堆分配的文件
QFile* compressedFile = new QFile(...);
QNetworkReply* reply = Manager.post(...);
// This is where the tricks is
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater());
connect(reply, SIGNAL(destroyed()), compressedFile, SLOT(deleteLater());

恕我直言,它比在外层保留你的文件更加本地化和封装。

请注意,如果您有connect()广告位,则必须先删除第一个postFinished(QNetworkReply*),然后您必须在其中忘记在其中调用reply->deleteLater(),以便上述工作完成。