Downloading File in Qt From URL

一世执手 提交于 2019-12-30 02:17:25

问题


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

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

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

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.


回答1:


  • You're using uninitialized pointer, so it points out to nowhere. Initialize reply with NULL in your constructor.
  • You should connect reply after it was created (reply = manager.get(...)), not inside of your constructor.
  • QNetworkReply is never deleted by QNetworkManager as docs say:

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

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

  • 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.

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.




回答2:


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

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 data inside 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 reply 3 times.

Here's the modified version :

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



回答3:


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;
    

Omit the if. Deleting a null pointer is safe.



来源:https://stackoverflow.com/questions/4383864/downloading-file-in-qt-from-url

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!