Загрузка файла в Qt из URL

В моей программе мне нужно скачать файл, и я наткнулся на эту статью:

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 ответов


  • вы используете неинициализированный указатель, поэтому он указывает в никуда. Инициализировать reply с NULL в конструкторе.
  • вы должны подключить reply после его создания (reply = manager.get(...)), а не внутри вашего конструктора.
  • QNetworkReply не удалены QNetworkManager as документы говорят:

не удаляйте объект ответа в слоте, подключенном к этому сигналу. Использовать deleteLater().

поэтому вы не должны вызывать delete on QNetworkReply на finished слот.

  • на finished настройка слота data to 0 будет только установить значение параметра 0, не ваш член класса reply. Это ненужная строка кода. Вы должны установить reply члены NULL вместо.

также вы должны рассмотреть возможность записи в файл каждый раз, когда вы получаете кусок данных, так как весь файл будет буферизован в памяти в вашем текущее дело. Это может привести к огромному использованию памяти вашего программного обеспечения, когда файл по указанному URL большой.


вам нужно 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:: новая версия

Я нашел некоторые проблемы :

  1. вам не нужен пользовательский ответ, также вы никогда не устанавливали его в 0 в своем конструкторе, поэтому, если он никогда не использовался, он удалит случайный кусок памяти в вашем ~QtDownload ();
  2. вы удаляли data внутри QtDownload::downloadFinished, которые не должны быть сделано, он обрабатывается Qt, поэтому он был удален дважды.
  3. из-за #2, удалялось reply 3 раза.

вот измененная версия :

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

Как вы просили, некоторые общие замечания:

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. удаление данных аргументов из слота опасно. Вы не знаете, продолжает ли сетевой менеджер использовать (или удалять) объект "данные" сразу после того, как он выдает готовый сигнал. Вероятно, вам даже не нужно удалять ответ, если он принадлежит менеджеру, что-то проверить в документации для.
  3. если не удается открыть файлы, данные не удаляются. Поэтому все, что правильно, непоследовательно. Либо утечка, либо риск двойного удаления.
  4. localFile.write (data->readAll ()) не гарантирует одновременную запись всех данных. вот почему она имеет возвращаемое значение, которое вы должны проверить, чтобы убедиться, что все написано. Если он возвращает -1, вы должны обработать ошибку.

    if (reply != 0)
        delete reply;
    

опустить if. Удаление указателя null безопасный.