C++ 从 URL 下载 Qt 中的文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4383864/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:16:51  来源:igfitidea点击:

Downloading File in Qt From URL

c++qtqt4

提问by Blair Harris

In my program I need to download a file, and I came across this article:

在我的程序中,我需要下载一个文件,我看到了这篇文章:

http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm

http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm

This code does work but it doesn't fit into my program so I re-coded it. I haven't completed it all but I've got the basics coded. However, when I test it, it pops up with a send error report window.

这段代码确实有效,但它不适合我的程序,所以我重新编码了它。我还没有完成这一切,但我已经编码了基础知识。但是,当我测试它时,它会弹出一个发送错误报告窗口。

So far this is my code:

到目前为止,这是我的代码:

QtDownload.h

Qt下载文件

#include <QObject>
#include <QString>
#include <QNetworkAccessManager>
#include <QNetworkReply>


class QtDownload : public QObject
{
    Q_OBJECT
public:
    explicit QtDownload();
    ~QtDownload();

    void setTarget(const QString& t);

private:
    QNetworkAccessManager manager;
    QNetworkReply* reply;
    QString target;
    void connectSignalsAndSlots();

signals:

public slots:
    void download();
    void downloadFinished(QNetworkReply* data);
    void downloadProgress(qint64 recieved, qint64 total);
};

QtDownload.cpp

Qt下载文件

#include "qtdownload.h"

#include <QUrl>
#include <QNetworkRequest>
#include <QFile>

QtDownload::QtDownload()
    : QObject(0)
{
    this->connectSignalsAndSlots();
}

QtDownload::~QtDownload()
{
    if (reply != 0)
        delete reply;
}

void QtDownload::connectSignalsAndSlots()
{
    QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(downloadFinished(QNetworkReply*)));
    QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
}

void QtDownload::setTarget(const QString &t)
{
    this->target = t;
}

void QtDownload::downloadFinished(QNetworkReply *data)
{
    QFile localFile("downloadedfile");
    if (!localFile.open(QIODevice::WriteOnly))
        return;
    localFile.write(data->readAll());
    localFile.close();
    delete data;
    data = 0;
}

void QtDownload::download()
{
    QUrl url = QUrl::fromEncoded(this->target.toLocal8Bit());
    QNetworkRequest request(url);
    this->reply = manager.get(request);
}

void QtDownload::downloadProgress(qint64 recieved, qint64 total)
{

}

main.cpp

主程序

#include "qtdownload.h"
#include <QTimer>

int main()
{
    QtDownload dl;
    dl.setTarget("http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm");

    QTimer::singleShot(0, &dl, SLOT(download()));
}

As I said it's not completely finished but I want this part to be working before I move on.

正如我所说,它还没有完全完成,但我希望在我继续之前这部分工作正常。

I'm also new to Qt so any tips would be appreciated.

我也是 Qt 的新手,所以任何提示都将不胜感激。

采纳答案by Kamil Klimek

  • You're using uninitialized pointer, so it points out to nowhere. Initialize replywith NULLin your constructor.
  • You should connect replyafter it was created (reply = manager.get(...)), not inside of your constructor.
  • QNetworkReplyis never deleted by QNetworkManageras docs say:
  • 您正在使用未初始化的指针,因此它指向无处。初始化replyNULL在你的构造。
  • 你应该reply在它被创建后连接( reply = manager.get(...)),而不是在你的构造函数内部。
  • QNetworkReplyQNetworkManager正如文档所说,永远不会被删除:

Do not delete the reply object in the slot connected to this signal. Use deleteLater().

不要删除连接到该信号的槽中的回复对象。使用 deleteLater()。

So you shouldn't call delete on QNetworkReplyin finishedslot.

所以你不应该QNetworkReplyfinished插槽中调用 delete 。

  • In finishedslot setting datato 0will only set parameter value to 0, not your class member reply. It's an unneeded line of code. You should set your replymember to NULLinstead.
  • finishedslot setting datato 中0只会将参数值设置为0,而不是您的班级成员reply。这是一行不需要的代码。您应该将您的reply成员设置为NULL

Also you should consider writing to a file every time you get data chunk, as whole file will be buffered in memory in your current case. It may lead to huge memory usage of your software when file at pointed URL is big.

此外,您应该考虑在每次获取数据块时写入文件,因为在您当前的情况下,整个文件将缓冲在内存中。当指向 URL 的文件很大时,它可能会导致您的软件占用大量内存。

回答by OneOfOne

You need QCoreApplicationto start the event loop for Qt4. Something like this should work (not tested) :

您需要QCoreApplication来启动 Qt4 的事件循环。这样的事情应该工作(未测试):

int main(int argc, char **argv) {
    QCoreApplication app(argc, argv);
    QtDownload dl;
    dl.setTarget("http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm");

    dl.download();
    QObject::connect(app, SIGNAL(aboutToQuit()), app, SLOT(quit()));
    return app.exec();
}

edit :: new version

编辑::新版本

I found some problems :

我发现了一些问题:

  1. You don't need the custom reply, also you never set it to 0 in your constructor, so if it was never used it will delete a random piece of memory in your ~QtDownload();
  2. you were deleting datainside QtDownload::downloadFinished, which shouldn't be done, it is handled by Qt, so it was getting deleted twice.
  3. because of #2, you were deleting reply3 times.
  1. 您不需要自定义回复,也从未在构造函数中将其设置为 0,因此如果从未使用过它,它将删除 ~QtDownload() 中的随机内存;
  2. 您正在删除datainside QtDownload::downloadFinished,这不应该完成,它由 Qt 处理,因此它被删除了两次。
  3. 由于#2,您删除了reply3 次。

Here's the modified version :

这是修改后的版本:

qtdownload.h:

qtdownload.h:

#include <QObject>
#include <QString>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>


class QtDownload : public QObject {
    Q_OBJECT
public:
    explicit QtDownload();
    ~QtDownload();

    void setTarget(const QString& t);

private:
    QNetworkAccessManager manager;
    QString target;

signals:
    void done();

public slots:
    void download();
    void downloadFinished(QNetworkReply* data);
    void downloadProgress(qint64 recieved, qint64 total);
};

qtdownload.cpp:

qtdownload.cpp:

#include "qtdownload.h"
#include <QCoreApplication>
#include <QUrl>
#include <QNetworkRequest>
#include <QFile>
#include <QDebug>

QtDownload::QtDownload() : QObject(0) {
    QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(downloadFinished(QNetworkReply*)));
}

QtDownload::~QtDownload() {

}


void QtDownload::setTarget(const QString &t) {
    this->target = t;
}

void QtDownload::downloadFinished(QNetworkReply *data) {
    QFile localFile("downloadedfile");
    if (!localFile.open(QIODevice::WriteOnly))
        return;
    const QByteArray sdata = data->readAll();
    localFile.write(sdata);
    qDebug() << sdata;
    localFile.close();

    emit done();
}

void QtDownload::download() {
    QUrl url = QUrl::fromEncoded(this->target.toLocal8Bit());
    QNetworkRequest request(url);
    QObject::connect(manager.get(request), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));

}

void QtDownload::downloadProgress(qint64 recieved, qint64 total) {
    qDebug() << recieved << total;
}

main.cpp:

主.cpp:

#include <QtCore>
#include "qtdownload.h"
int main(int argc, char **argv) {
    QCoreApplication app(argc, argv);
    QtDownload dl;
    dl.setTarget("http://localhost");

    dl.download();
    //quit when the download is done.
    QObject::connect(&dl, SIGNAL(done()), &app, SLOT(quit()));
    return app.exec();
}

回答by Frank Osterfeld

As you asked for it, some general comments:

正如你所要求的,一些一般性评论:

void QtDownload::downloadFinished(QNetworkReply *data)
{
    QFile localFile("downloadedfile");
    if (!localFile.open(QIODevice::WriteOnly))
        return;
    localFile.write(data->readAll());
    localFile.close();
    delete data;
    data = 0;
}
  1. You read all data in one chunk. Bad for big files. Better read it incrementally.
  2. Deleting the argument data from a slot is dangerous. You don't know whether the network manager continues to use (or delete) the object "data" points to right after it emits the finished signal. Probably you don't even have to delete the reply, if its owned by the manager, something to check the documentation for.
  3. If opening the files fails, data is not deleted. So whatever is correct, its inconsistent. Either you leak or you have the risk of double-deletion.
  4. localFile.write(data->readAll()) is not guaranteed to write all data at once. that's why it has a return value, which you should check, to make sure everything is written. If it returns -1, you should handle the error.

    if (reply != 0)
        delete reply;
    
  1. 您在一个块中读取所有数据。不适合大文件。最好循序渐进地阅读它。
  2. 从插槽中删除参数数据是危险的。您不知道网络管理器在发出完成信号后是否继续使用(或删除)“数据”指向的对象。可能您甚至不必删除回复,如果它归经理所有,则可以检查文档。
  3. 如果打开文件失败,则不会删除数据。所以无论是正确的,都是不一致的。要么你泄漏,要么你有双重删除的风险。
  4. localFile.write(data->readAll()) 不能保证一次写入所有数据。这就是为什么它有一个返回值,你应该检查它,以确保一切都被写入。如果它返回 -1,您应该处理错误。

    if (reply != 0)
        delete reply;
    

Omit the if. Deleting a null pointer is safe.

省略如果。删除空指针是安全的。