Скачивание файла в Qt с URL - PullRequest
8 голосов
/ 08 декабря 2010

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

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 ]

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

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

Так что вам не следует вызывать delete в QNetworkReply в finished слоте.

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

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

2 голосов
/ 08 декабря 2010

Вам нужно 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();
}
1 голос
/ 08 декабря 2010

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

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. Удаление нулевого указателя безопасно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...