Downloading File in Qt From URL

后端 未结 3 850
半阙折子戏
半阙折子戏 2020-12-31 17:43

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 d

相关标签:
3条回答
  • 2020-12-31 17:54

    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.

    0 讨论(0)
  • 2020-12-31 18:02
    • 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.

    0 讨论(0)
  • 2020-12-31 18:11

    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();
    }
    
    0 讨论(0)
提交回复
热议问题