我在Qt应用程序中遇到了一些麻烦。 特别是与QNetworkAccessManager类。 我正在尝试使用QNetworkAccessManager的post()方法执行二进制文件的简单HTTP上载。 该文档指出,我可以给QIODevice一个指针post(),并且该类将传输在QIODevice中find的数据。 这表明我应该能够给post()一个指向QFile的指针。 例如:
QFile compressedFile("temp"); compressedFile.open(QIODevice::ReadOnly); netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), &compressedFile);
我正在开发的Windows系统上似乎发生了什么,我的Qt应用程序从QFile中推送数据,但不完成请求; 它似乎坐在那里等待更多的数据显示从文件。 发布请求不是“closures”,直到我手动杀死应用程序,此时整个文件显示在我的服务器端。
从一些debugging和研究中,我认为这是因为QFile的read()操作在到达文件末尾时不返回-1。 我认为QNetworkAccessManager试图从QIODevice中读取,直到它从read()中得到-1,此时它假定没有更多的数据并closures请求。 如果从read()得到的返回码为零,QNetworkAccessManager假定可能有更多的数据到来,所以它一直在等待这个假设的数据。
我已经用一些testing代码证实了,在你读完文件后,QFile的read()操作只返回0。 这似乎与QNetworkAccessManager的post()方法期望QIODevice行为的方式不兼容。 我的问题是:
任何build议或提示将不胜感激。
更新:事实certificate,我有两个不同的问题:一个在客户端和一个在服务器端。 在客户端,我必须确保我的QFile对象在networking事务期间保持闲置状态。 QNetworkAccessManager的post()方法立即返回,但实际上并没有立即完成。 您需要在QNetworkAccessManager的finished()信号上附加一个插槽,以确定POST何时实际完成。 在我的情况下,QFile几乎是永久的保存起来很容易,但是我也为完成的()信号附加了一个插槽,以便检查来自服务器的错误响应。
我将信号附加到插槽中,如下所示:
connect(&netManager, SIGNAL(finished(QNetworkReply*) ), this, SLOT(postFinished(QNetworkReply*) ) );
当我发送文件的时候,我写了这样的post代码(注意:compressedFile是我的类的成员,所以在代码之后不会超出范围):
compressedFile.open(QIODevice::ReadOnly); netManager.post(QNetworkRequest(QUrl(httpDestination.getCString() ) ), &compressedFile);
QNetworkAccessManager完成(QNetworkReply *)信号触发我的postFinished(QNetworkReply *)方法。 发生这种情况时,closurescompressedFile并删除compressedFile表示的数据文件是安全的。 为了进行debugging,我还添加了一些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没有立即closures,也没有超出范围,QNetworkAccessManager能够花费尽可能多的时间来传输我的文件。 最终交易完成,我的postFinished()方法被调用。
我的另一个问题(也是导致我看到事务从未完成的行为的原因)是我的Web服务器的Python代码没有正确地置入POST,但这超出了我原来的Qt问题的范围。
你正在栈上创建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);
您也可以使用信号/插槽安排自动删除堆分配的文件
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());
恕我直言,它是更多的本地化和封装,而不是必须保持你的文件在外部类。
注意,如果你有postFinished(QNetworkReply*)
插槽,那么你必须先删除第一个connect()
,然后你不能忘记在里面调用reply->deleteLater()
来使上面的工作成功。