从URL下载Qt文件。

时间:2021-06-12 09:56:01

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

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

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

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的新手,所以任何建议都会被欣赏。

3 个解决方案

#1


5  

  • You're using uninitialized pointer, so it points out to nowhere. Initialize reply with NULL in your constructor.
  • 你使用的是未初始化的指针,所以它指向了什么地方都没有。在构造函数中初始化回复与NULL。
  • You should connect reply after it was created (reply = manager.get(...)), not inside of your constructor.
  • 您应该在创建后连接应答(reply = manager.get(…)),而不是在构造函数内部。
  • QNetworkReply is never deleted by QNetworkManager as docs say:
  • QNetworkReply从未被QNetworkManager删除,文档说明:

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

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

So you shouldn't call delete on QNetworkReply in finished slot.

因此,您不应该在完成的slot中调用QNetworkReply的delete。

  • In finished slot setting data to 0 will only set parameter value to 0, not your class member reply. It's an unneeded line of code. You should set your reply member to NULL instead.
  • 在完成的slot设置数据为0时,只将参数值设置为0,而不是类成员的回复。这是一条不需要的代码。您应该将回复成员设置为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的文件很大时,它可能会导致大量内存使用。

#2


1  

You need QCoreApplication to 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. 您不需要自定义的回复,也不需要在构造函数中设置为0,因此如果它从未使用过,它将在您的~QtDownload()中删除一个随机的内存块;
  3. you were deleting data inside QtDownload::downloadFinished, which shouldn't be done, it is handled by Qt, so it was getting deleted twice.
  4. 你删除了QtDownload中的数据::downloadFinished,这是不应该做的,它是由Qt处理的,所以它被删除了两次。
  5. because of #2, you were deleting reply 3 times.
  6. 因为#2,你正在删除回复3次。

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

#3


1  

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. 你把所有的数据都读了一大块。对大文件。更好的增量地读它。
  3. 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.
  4. 从槽中删除参数数据是危险的。您不知道网络管理器是否继续使用(或删除)对象“数据”点,在它发出完成的信号之后。可能您甚至不需要删除该回复,如果它属于管理员,就可以检查文档。
  5. 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.
  6. 如果打开文件失败,数据不会被删除。所以无论什么是正确的,它都是不一致的。要么你泄漏,要么你有双重删除的风险。
  7. 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.

    写入(data->readAll())不能保证立即写入所有数据。这就是为什么它有一个返回值,你应该检查它,以确保所有的东西都写好了。如果返回-1,则应该处理错误。

    if (reply != 0)
        delete reply;
    

Omit the if. Deleting a null pointer is safe.

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

#1


5  

  • You're using uninitialized pointer, so it points out to nowhere. Initialize reply with NULL in your constructor.
  • 你使用的是未初始化的指针,所以它指向了什么地方都没有。在构造函数中初始化回复与NULL。
  • You should connect reply after it was created (reply = manager.get(...)), not inside of your constructor.
  • 您应该在创建后连接应答(reply = manager.get(…)),而不是在构造函数内部。
  • QNetworkReply is never deleted by QNetworkManager as docs say:
  • QNetworkReply从未被QNetworkManager删除,文档说明:

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

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

So you shouldn't call delete on QNetworkReply in finished slot.

因此,您不应该在完成的slot中调用QNetworkReply的delete。

  • In finished slot setting data to 0 will only set parameter value to 0, not your class member reply. It's an unneeded line of code. You should set your reply member to NULL instead.
  • 在完成的slot设置数据为0时,只将参数值设置为0,而不是类成员的回复。这是一条不需要的代码。您应该将回复成员设置为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的文件很大时,它可能会导致大量内存使用。

#2


1  

You need QCoreApplication to 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. 您不需要自定义的回复,也不需要在构造函数中设置为0,因此如果它从未使用过,它将在您的~QtDownload()中删除一个随机的内存块;
  3. you were deleting data inside QtDownload::downloadFinished, which shouldn't be done, it is handled by Qt, so it was getting deleted twice.
  4. 你删除了QtDownload中的数据::downloadFinished,这是不应该做的,它是由Qt处理的,所以它被删除了两次。
  5. because of #2, you were deleting reply 3 times.
  6. 因为#2,你正在删除回复3次。

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

#3


1  

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. 你把所有的数据都读了一大块。对大文件。更好的增量地读它。
  3. 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.
  4. 从槽中删除参数数据是危险的。您不知道网络管理器是否继续使用(或删除)对象“数据”点,在它发出完成的信号之后。可能您甚至不需要删除该回复,如果它属于管理员,就可以检查文档。
  5. 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.
  6. 如果打开文件失败,数据不会被删除。所以无论什么是正确的,它都是不一致的。要么你泄漏,要么你有双重删除的风险。
  7. 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.

    写入(data->readAll())不能保证立即写入所有数据。这就是为什么它有一个返回值,你应该检查它,以确保所有的东西都写好了。如果返回-1,则应该处理错误。

    if (reply != 0)
        delete reply;
    

Omit the if. Deleting a null pointer is safe.

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