Загрузка файла QT с помощью QNetworkAccessManager

Я пытаюсь заставить код в этом вопросе работать. https://stackoverflow.com/questions/6285661/qt-how-to-download-and-save-image-via-http

Он создает файлы, но данные в них не записываются. Я думаю, что подключение к синглам и слотам не работает. Что я делаю не так?

главное окно.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    QString getPageImage(QString url = "");
    bool saveToDisk(const QString &filename, QIODevice *data);
    ~MainWindow();

private slots:
    void on_btnDownload_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

qdownload.h

#ifndef QDOWNLOADER_H
#define QDOWNLOADER_H

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QFile>
#include <QStringList>
#include <QDir>
#include <QDebug>

class QDownloader : public QObject
{
    Q_OBJECT
public:
    explicit QDownloader(QObject *parent = 0);
    virtual ~QDownloader();
    void setFile(QString fileURL, QString folderName, QString fileName);

private:
    QNetworkAccessManager *manager;
    QNetworkReply *reply;
    QFile *file;

private slots:
    void onDownloadProgress(qint64,qint64);
    void onFinished(QNetworkReply*);
    void onReadyRead();
    void onReplyFinished();
};

#endif // QDOWNLOADER_H

главное окно.cpp

   #include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QNetworkAccessManager"
#include "QNetworkReply"
#include "QFile"
#include "QDebug"
#include "QUrl"
#include "QStringList"
#include "qdownloader.h"
#include <QTimer>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->progressBar->setVisible(false);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_btnDownload_clicked()
{
    QString link = ui->txtURL->text();
    int issue = ui->sbIssue->value();
    ui->progressBar->setMaximum(issue);
    ui->progressBar->setValue(0);
    ui->progressBar->setVisible(true);
    for(int j = 1; j <= issue; j++){
        int i = 1;
        while(getPageImage(link + "/" + QString::number(j) + "/" + QString::number(i) + "/").length() > 0){
            QString filename = "http://" + getPageImage(link + "/" + QString::number(j) + "/" + QString::number(i) + "/");
            qDebug() << filename;
            QDownloader qDL;
            qDL.setFile(filename,ui->txtTitle->text() + "_" + QString::number(j),ui->txtTitle->text() + "_" + QString::number(j) + "_" + QString::number(i));
            //qDebug() << getPageImage(link + "/" + QString::number(j) + "/" + QString::number(i) + "/").length();
            i++;
        }
        ui->progressBar->setValue(j);
    }
    ui->progressBar->setVisible(false);
}


QString MainWindow::getPageImage(QString url){
    QString pic = "";
    if(url.length() > 0){
        QNetworkAccessManager manager;
        QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));
        QEventLoop event;
        connect(response,SIGNAL(finished()),&event,SLOT(quit()));
        event.exec();
        QString html = response->readAll();

        if(html.length() > 0){
            QStringList str;
            str = html.split("\n");
            //qDebug() << url;

            for (int i = 0; i < str.size(); ++i){
                if(str.at(i).contains("id=\"mainImg\"", Qt::CaseInsensitive)){

                    pic = str.at(i);
                    pic = pic.remove(QRegExp("<img[^>]*src=['|\"]",  Qt::CaseInsensitive));
                    pic = pic.remove(QString::fromStdString("//"), Qt::CaseInsensitive);
                    pic = pic.remove('"');
                    pic = pic.remove("'");
                    pic = pic.remove('<');
                    pic = pic.remove('>');
                    pic = pic.remove(';');
                    pic = pic.left(pic.length()-1);
                    //qDebug() << str.at(i);
                    //qDebug() << pic;
                }
            }
        }else{
            pic = "";
        }
        //qDebug() << "Lines: " << str.size();

    }else{
        pic = "";
    }
    return pic;
}

qdownload.cpp

#include "qdownloader.h"

QDownloader::QDownloader(QObject *parent) :
    QObject(parent)
{
    manager = new QNetworkAccessManager;
}

QDownloader::~QDownloader()
{
    manager->deleteLater();
}

void QDownloader::setFile(QString fileURL, QString folderName, QString fileName)
{

    QDir dir;
    if(dir.exists(folderName)){
        //qDebug() << "Existis: " + folderName;
    }else{
        dir.mkdir(folderName);
        //() << "Created: " + folderName;
    }

    QString filePath = fileURL;
    QStringList filePathList = filePath.split('/');
    QString fileExt = filePathList.at(filePathList.count() - 1);
    fileExt = "jpg";
    QString saveFilePath;
    saveFilePath = QString(folderName + "/" + fileName + "." + fileExt );



    QNetworkRequest request;
    request.setUrl(QUrl(fileURL));
    reply = manager->get(request);



    file = new QFile;
    file->setFileName(saveFilePath);


    connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(onDownloadProgress(qint64,qint64)));
    connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(onFinished(QNetworkReply*)));
    connect(reply,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
    connect(reply,SIGNAL(finished()),this,SLOT(onReplyFinished()));
}

void QDownloader::onDownloadProgress(qint64 bytesRead,qint64 bytesTotal)
{
    qDebug(QString::number(bytesRead).toLatin1() +" - "+ QString::number(bytesTotal).toLatin1());
}

void QDownloader::onFinished(QNetworkReply * reply)
{
    switch(reply->error())
    {
        case QNetworkReply::NoError:
        {
            qDebug("file is downloaded successfully.");
        }break;
        default:{
            qDebug(reply->errorString().toLatin1());
        };
    }

    if(file->isOpen())
    {
        file->close();
        file->deleteLater();
    }
}

void QDownloader::onReadyRead()
{
    qDebug() << "Ready";
    file->open(QIODevice::WriteOnly);
    file->write(reply->readAll());
}

void QDownloader::onReplyFinished()
{
    if(file->isOpen())
    {
        file->close();
        file->deleteLater();
    }
}

person Talon06    schedule 15.10.2014    source источник
comment
@lpapp, пожалуйста, проверьте это еще раз. Не думаю, что это ответит на этот вопрос.   -  person mhcuervo    schedule 16.10.2014
comment
@mhcuervo: Я снова открыл это для вашего удовольствия, но, по моему скромному мнению, это было на грани.   -  person lpapp    schedule 16.10.2014


Ответы (1)


Я подготовил очень простой пример, который вы можете использовать в качестве справки.

В каком-то месте в "своем" загрузчике запускаю запрос:

QNetworkRequest request;
request.setUrl(org);
_reply = _manager->get(request); // Manager is my QNetworkAccessManager
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)),
            this, SLOT(error(QNetworkReply::NetworkError)));
connect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
            this, SLOT(updateProgress(qint64, qint64)));
connect(_reply, SIGNAL(finished()),
            this, SLOT(finished()));

Как видите, все, что вам нужно сделать, это get() и правильно использовать сигналы QNetworkReply. Следующие «мои» слоты:

void MyDownloader::error(QNetworkReply::NetworkError err)
{
    // Manage error here.
    _reply->deleteLater();
}

void MyDownloader::updateProgress(qint64 read, qint64 total)
{
    // This is where you can use the progress info for, lets say, update a progress bar:
    // progressBar->setMaximum(total);
    // progressBar->setValue(read);
}

void MyDownloader::finished()
{
    // Save the image here
    QByteArray b = _reply->readAll();
    QFile file(des); // "des" is the file path to the destination file
    file.open(QIODevice::WriteOnly);
    QDataStream out(&file);
    out << b;
    _reply->deleteLater();
    // done
}

Обратите внимание, что вы можете писать в файл в updateProgress() вместо записи всего файла сразу после завершения ответа.


Чтобы написать updateProgress(), откройте и/или создайте QFile в get времени. Затем используйте ответ readAll при каждом обновлении и записывайте прочитанные данные в файл, а затем закройте файл, когда finished.

// ...
    _file = new QFile(des);
    _reply = _manager->get(request);
/// ...

void MyDownloader::updateProgress(qint64 read, qint64 total)
{
    QByteArray b = _reply->readAll();
    QDataStream out(_file);
    out << b;
}

void MyDownloader::finished()
{
    // Done
    _reply->deleteLater();
    _file->close();
    // probably delete the file object too
}
person mhcuervo    schedule 16.10.2014
comment
Как мне написать файл в функции updateProgress()? - person Talon06; 16.10.2014
comment
Уже есть ответ = manager->get(request) на запрос QNetworkRequest; request.setUrl (QUrl (URL-адрес файла)); ответ = менеджер-> получить (запрос); - person Talon06; 16.10.2014
comment
Спасибо, я обновил свой код, чтобы писать во время загрузки. Однако проблема, похоже, связана с сигналами и слотами. Почему-то не стреляют. У меня есть qDebug в каждом слоте, и ни один из них не появляется при запуске программы. - person Talon06; 16.10.2014
comment
Какую версию Qt вы используете? - person mhcuervo; 16.10.2014
comment
Qt Creator 3.2.1 (с открытым исходным кодом) На основе Qt 5.3.2 (MSVC 2010, 32-разрядная версия) Создан 14 сентября 2014 г., 04:14:50 Из редакции 602746950b Copyright 2008-2014 Digia Plc. Все права защищены. Программа предоставляется КАК ЕСТЬ, без КАКИХ-ЛИБО ГАРАНТИЙ, ВКЛЮЧАЯ ГАРАНТИИ ДИЗАЙНА, КОММЕРЧЕСКОЙ ПРИГОДНОСТИ И ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ. - person Talon06; 16.10.2014
comment
Хммм... Я вижу в вашем коде, что qDL выходит за рамки сразу после вызова setFile, что приводит к уничтожению объекта. Попробуйте заставить setFile ждать завершения ответа перед возвратом, добавьте это в конец вашего setFile: QEventLoop eventLoop; соединение(ответ, СИГНАЛ(завершено()), &eventLoop, SLOT(выйти())); цикл событий.exec(); - person mhcuervo; 16.10.2014
comment
Проголосовал против, потому что вся загрузка хранится в памяти перед записью в файл. Это можно исправить путем потоковой передачи данных непосредственно в файл (моя загрузка объемом 4 ГБ не помещалась в память). - person feedc0de; 04.06.2017