在Qt中从URL下载文件

9
在我的程序中,我需要下载一个文件,并且我找到了这篇文章:http://www.java2s.com/Code/Cpp/Qt/DownloadfromURL.htm。这段代码确实可以工作,但它并不适合我的程序,所以我重新编写了它。虽然我还没有完全完成它,但我已经编写了基本部分。然而,当我测试它时,它会弹出一个发送错误报告窗口。
到目前为止,这是我的代码:
QtDownload.h
#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

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

就像我说的,这还没有完全完成,但我希望在继续进行之前能使这部分工作正常运行。

我也是Qt的新手,如果您有任何提示将不胜感激。

3个回答

8
  • 你正在使用未初始化的指针,所以它指向了无处。在构造函数中用NULL初始化reply
  • 你应该在创建reply之后连接它(reply = manager.get(...)),而不是在构造函数内部连接。
  • QNetworkReply永远不会被QNetworkManager删除,就像文档中所说的那样

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

因此,在finished槽中不应该调用QNetworkReply的delete方法。

  • finished槽中将data设置为0只会将参数值设置为0,而不是你的类成员reply。这是一行不必要的代码。你应该将reply成员设置为NULL

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


3

为了启动Qt4的事件循环,您需要使用QCoreApplication。以下代码应该可以实现(未经测试):

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

编辑 :: 新版本

我发现了一些问题:

  1. 您不需要自定义回复,也从未在构造函数中将其设置为0,因此如果从未使用它,则会在您的~QtDownload()中删除随机的内存。
  2. 您在QtDownload :: downloadFinished内部删除了data,这是不应该做的,因为它由Qt处理,所以它被删除了两次。
  3. 由于问题#2,您删除了reply三次。

这是修改后的版本:

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 :

#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 :

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

在我的回答中有解释。主要是关于“Qt 处理它”。 - Kamil Klimek
感谢回复和建议,我现在会进行测试并回复。 - Blair Harris
OneOfOne 写的代码确实可以工作。只是一个展示我的经验不足的问题,第10行在下载后退出了应用程序,如果我将其注释掉,那么它会在完成下载之前继续执行下一行,并且如果是这样,那么我如何使其等待直到完成已被发出?另外,如果我逐步写入数据块,如果数据很大(在此程序中文件仅为2MB),我将使用哪个信号呢?我查看了 QT 文档,但没有找到实际处理每次接收数据块的信号?谢谢。 - Blair Harris
在 main.cpp 中,你应该先连接,然后再下载。通常,在建立连接之前不要生成新的线程。 - marcelnijman

1

正如您所要求的那样,以下是一些一般性的评论:

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. 你一次性读取所有数据。对于大文件来说不好。最好逐步增量读取。
  2. 从插槽中删除参数数据是危险的。你不知道网络管理器在发出完成信号后是否继续使用(或删除)"data"指向的对象。如果它是由管理器拥有的,可能甚至不需要删除回复,需要查看文档。
  3. 如果打开文件失败,数据不会被删除。所以无论哪种情况都是不一致的。要么泄漏,要么有双重删除的风险。
  4. localFile.write(data->readAll())不能保证一次写入所有数据。这就是为什么它有一个返回值,你应该检查它,确保所有内容都被写入。如果返回-1,你应该处理错误。

    if (reply != 0)
        delete reply;
    
省略 if。删除空指针是安全的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接