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

发布于 2024-08-31 15:34:37 字数 2465 浏览 1 评论 0原文

我在使用 Qt 应用程序时遇到一些问题;特别是 QNetworkAccessManager 类。我正在尝试使用 QNetworkAccessManager 的 post() 方法执行二进制文件的简单 HTTP 上传。文档指出我可以为 post() 提供一个指向 QIODevice 的指针,并且该类将传输在 QIODevice 中找到的数据。这表明我应该能够为 post() 提供一个指向 QFile 的指针。例如:

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

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

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

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

  1. QFile 在 Windows 下的工作方式是否存在某种限制?
  2. 我是否应该使用 QFile 或 QNetworkAccessManager 通过 post() 推送文件?
  3. 这根本不起作用吗?我是否必须找到其他方法来上传我的文件?

任何建议或提示将不胜感激。

更新:事实证明我遇到了两个不同的问题:一个在客户端,一个在服务器端。在客户端,我必须确保我的 QFile 对象在网络事务期间保持不变。 QNetworkAccessManager 的 post() 方法立即返回,但实际上并没有立即完成。您需要将一个槽附加到 QNetworkAccessManager 的 finish() 信号,以确定 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 的 finish(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 问题的范围。

I'm having some trouble with a Qt application; specifically with the QNetworkAccessManager class. I'm attempting to perform a simple HTTP upload of a binary file using the post() method of the QNetworkAccessManager. The documentation states that I can give a pointer to a QIODevice to post(), and that the class will transmit the data found in the QIODevice. This suggests to me that I ought to be able to give post() a pointer to a QFile. For example:

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

What seems to happen on the Windows system where I'm developing this is that my Qt application pushes the data from the QFile, but then doesn't complete the request; it seems to be sitting there waiting for more data to show up from the file. The post request isn't "closed" until I manually kill the application, at which point the whole file shows up at my server end.

From some debugging and research, I think this is happening because the read() operation of QFile doesn't return -1 when you reach the end of the file. I think that QNetworkAccessManager is trying to read from the QIODevice until it gets a -1 from read(), at which point it assumes there is no more data and closes the request. If it keeps getting a return code of zero from read(), QNetworkAccessManager assumes that there might be more data coming, and so it keeps waiting for that hypothetical data.

I've confirmed with some test code that the read() operation of QFile just returns zero after you've read to the end of the file. This seems to be incompatible with the way that the post() method of QNetworkAccessManager expects a QIODevice to behave. My questions are:

  1. Is this some sort of limitation with the way that QFile works under Windows?
  2. Is there some other way I should be using either QFile or QNetworkAccessManager to push a file via post()?
  3. Is this not going to work at all, and will I have to find some other way to upload my file?

Any suggestions or hints would be appreciated.

Update: It turns out that I had two different problems: one on the client side and one on the server side. On the client side, I had to ensure that my QFile object stayed around for the duration of the network transaction. The post() method of QNetworkAccessManager returns immediately but isn't actually finished immediately. You need to attach a slot to the finished() signal of QNetworkAccessManager to determine when the POST is actually finished. In my case it was easy enough to keep the QFile around more or less permanently, but I also attached a slot to the finished() signal in order to check for error responses from the server.

I attached the signal to the slot like this:

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

When it was time to send my file, I wrote the post code like this (note that compressedFile is a member of my class and so does not go out of scope after this code):

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

The finished(QNetworkReply*) signal from QNetworkAccessManager triggers my postFinished(QNetworkReply*) method. When this happens, it's safe for me to close compressedFile and to delete the data file represented by compressedFile. For debugging purposes I also added a few printf() statements to confirm that the transaction is complete:

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();  
}  

Since compressedFile isn't closed immediately and doesn't go out of scope, the QNetworkAccessManager is able to take as much time as it likes to transmit my file. Eventually the transaction is complete and my postFinished() method gets called.

My other problem (which also contributed to the behavior I was seeing where the transaction never completed) was that the Python code for my web server wasn't fielding the POST correctly, but that's outside the scope of my original Qt question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

知你几分 2024-09-07 15:34:38

您还可以使用信号/槽安排自动删除堆分配的文件

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() 在它里面以使上面的工作正常。

You can also schedule automatic deletion of a heap-allocated file using signals/slots

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());

IMHO, it is much more localized and encapsulated than having to keep around your file in the outer class.

Note that you must remove the first connect() if you have your postFinished(QNetworkReply*) slot, in which you must then not forget to call reply->deleteLater() inside it for the above to work.

¢好甜 2024-09-07 15:34:37

您正在堆栈上创建compressedFile,并将指向它的指针传递给您的QNetworkRequest(最终传递给您的QNetworkAccessManager)。一旦离开当前方法,compressedFile 就会超出范围。我很惊讶它没有让你崩溃,尽管行为是未定义的。

您需要在堆上创建QFile

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

您当然需要跟踪它,然后在发布完成后删除它,或者将其设置为子项QNetworkReply 的内容,以便稍后回复被销毁时它也会被销毁:

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

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

You're creating compressedFile on the stack, and passing a pointer to it to your QNetworkRequest (and ultimately your QNetworkAccessManager). As soon as you leave the method you're in, compressedFile is going out of scope. I'm surprised it's not crashing on you, though the behavior is undefined.

You need to create the QFile on the heap:

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

You will of course need to keep track of it and then delete it once the post has completed, or set it as the child of the QNetworkReply so that it it gets destroyed when the reply gets destroyed later:

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

QNetworkReply *reply = netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), compressedFile); 
compressedFile->setParent(reply);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文