简体   繁体   中英

qt - how to download and save image via http?

I am trying to download and save some images with Qt in a console appplication. Here is what I got so far, (all code is compiling, but after run, it seems to don't enter to replyFinished() function...)

void Test::start()
{
    std::cout << "start1";
    QNetworkAccessManager *manager = new QNetworkAccessManager();
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
    manager->get(QNetworkRequest(QUrl("http://www.exylum.mydevil.net/firefox.jpg")));
}

void Test::replyFinished(QNetworkReply* reply)
{
    std::cout << "st";
    QImage* img2 = new QImage();
    img2->loadFromData(reply->readAll());

    if(img2->isNull())
        std::cout << "oops";

    if(img2->save("omg2.jpg", "JPG"))
        std::cout << "saved";
    else
        std::cout << "dont...";
}

Download Image using QNetworkAccessManager

header file

#ifndef QDOWNLOADER_H
#define QDOWNLOADER_H

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

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

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

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

#endif // QDOWNLOADER_H

source file

#include "qdownloader.h"

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

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

void QDownloader::setFile(QString fileURL)
{
    QString filePath = fileURL;
    QString saveFilePath;
    QStringList filePathList = filePath.split('/');
    QString fileName = filePathList.at(filePathList.count() - 1);
    saveFilePath = QString("C:/Images/" + fileName );

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

    file = new QFile;
    file->setFileName(saveFilePath);
    file->open(QIODevice::WriteOnly);

    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()
{
    file->write(reply->readAll());
}

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

This is an old question, but for reference, I'll post a working version of @lwinhtooko's code:

downloader.h:

#pragma once

#include <QFile>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QObject>
#include <QUrl>

class Downloader : public QObject {
    Q_OBJECT
    QFile *m_file;
    bool m_isReady = true;

public:
    explicit Downloader(QObject *parent = 0) : QObject(parent) {}
    virtual ~Downloader() { delete m_file; }

    void downloadFileFromURL(const QString &url, const QString &filePath);

private slots:
    void onDownloadFileComplete(QNetworkReply *reply);
};

downloader.cpp:

#include "downloader.h"

void Downloader::downloadFileFromURL(const QString &url, const QString &filePath) {
    if (!m_isReady)
        return;
    m_isReady = false;

    const QString fileName = filePath + url.right(url.size() - url.lastIndexOf("/")); // your filePath should end with a forward slash "/"
    m_file = new QFile();
    m_file->setFileName(fileName);
    m_file->open(QIODevice::WriteOnly);
    if (!m_file->isOpen()) {
        m_isReady = true;
        return; // TODO: permission check?
    }

    QNetworkAccessManager *manager = new QNetworkAccessManager;

    QNetworkRequest request;
    request.setUrl(QUrl(url));

    connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(onDownloadFileComplete(QNetworkReply *)));

    manager->get(request);
}

void Downloader::onDownloadFileComplete(QNetworkReply *reply) {
    if (!m_file->isWritable()) {
        m_isReady = true;
        return; // TODO: error check
    }

    m_file->write(reply->readAll());
    m_file->close(); // TODO: delete the file from the system later on
    m_isReady = true;
}

The issue appeared to be the way QNetworkReply was being used.

None of above solution didn't work for me. But i found this Qt wiki page that worked perfectly for all type of data.

Download Data from URL

This Question is old, but I had a similar problem and now I have a solution for Qt 5.8.

The code is designed to work fast and the network requests are done in an asynchronous way. Therefore you have to provide an ID for each call to know which replay has finished.

Additionally, this code uses SSL. If you don't want to use SSL encryption delete the 4 lines with QSslConfiguartion.

filedownloader.h:

#ifndef FILEDOWNLOADER_H
#define FILEDOWNLOADER_H

#include <QObject>
#include <QStringList>
#include <QFile>
#include <QDir>

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>

class FileDownloader : public QObject
{
    Q_OBJECT

public:
    explicit FileDownloader(QObject *parent = 0);
    virtual ~FileDownloader();
    void downloadFile(QUrl url, QString id, QString dir_absolute_path);

signals:
    // emits error string
    void error(QString);
    // Emits path to img on disk and id
    void downloaded(QString, QString);

private slots:
    void fileDownloaded();
    void onReadyRead();

private:
    QNetworkAccessManager *webCtrl;
    QMap<QNetworkReply*, QFile*> replytofile;
    QMap<QNetworkReply*, QPair<QString, QString> > replytopathid;

    const QByteArray userAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36";
};

#endif // FILEDOWNLOADER_H

filedownloader.cpp:

#include "filedownloader.h"
#include <QDebug>

FileDownloader::FileDownloader(QObject *parent) :
    QObject(parent),
    webCtrl(new QNetworkAccessManager(this))
{

}

FileDownloader::~FileDownloader() 
{
    delete webCtrl;
}

void FileDownloader::downloadFile(QUrl url, QString id, QString dir_absolute_path)
{
    QString url_string = url.toString();
    QString path = dir_absolute_path + url_string.right(url_string.size() - url_string.lastIndexOf("/"));

    QFile *file = new QFile(path, this);
    if(!file->open(QIODevice::WriteOnly))
    {
        return;
    }

    QNetworkRequest request(url);
    request.setRawHeader("User-Agent", userAgent);

    QSslConfiguration sslConfiguration(QSslConfiguration::defaultConfiguration());
    sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
    sslConfiguration.setProtocol(QSsl::AnyProtocol);
    request.setSslConfiguration(sslConfiguration);

    QNetworkReply *reply = webCtrl->get(request);
    replytofile.insert(reply, file);
    replytopathid.insert(reply, QPair<QString, QString>(path, id));

    QObject::connect(reply, &QNetworkReply::finished, this, &FileDownloader::fileDownloaded);
    QObject::connect(reply, &QNetworkReply::readyRead, this, &FileDownloader::onReadyRead);
}

void FileDownloader::fileDownloaded()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());

    if (replytofile[reply]->isOpen())
    {
        replytofile[reply]->close();
        replytofile[reply]->deleteLater();
    }

    switch(reply->error())
    {
    case QNetworkReply::NoError:
        break;

    default:
        emit error(reply->errorString().toLatin1());
        break;
    }

    emit downloaded(replytopathid[reply].first, replytopathid[reply].second);

    replytofile.remove(reply);
    replytopathid.remove(reply);
    delete reply;
}

void FileDownloader::onReadyRead()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());

    replytofile[reply]->write(reply->readAll());
}

header file

#ifndef IMAGEDOWNLOAD_H
#define IMAGEDOWNLOAD_H

#include <QWidget>
#include <QHttp>
#include <QFile>
#include <QUrl>

class ImageDownload : public QWidget
{
    Q_OBJECT
public:
    explicit ImageDownload(QWidget *parent = 0);

private:
    int httpGetId;
    QHttp http;
    QFile myfile;

private slots:
    void httpRequestFinished(int, bool);
    void progress(int,int);
};

#endif // IMAGEDOWNLOAD_H

source file

#include "imagedownload.h"

ImageDownload::ImageDownload(QWidget *parent) :
    QWidget(parent)
{
    QUrl url("url of image.");

    myfile.setFileName("C:/Qt/imagefilename");
    myfile.open(QIODevice::WriteOnly);

    connect(&http,SIGNAL(requestFinished(int,bool)),this,SLOT(httpRequestFinished(int,bool)));
    connect(&http,SIGNAL(dataReadProgress(int,int)),this,SLOT(progress(int,int)));
    http.setHost(url.host(),QHttp::ConnectionModeHttp,url.port());
    httpGetId = http.get(url.path(),&myfile);
}

void ImageDownload::httpRequestFinished(int id, bool error)
{
    if(id == httpGetId)
    {
        myfile.close();
    }
    if(error)
    {
        qDebug(http.errorString().toLatin1());
    }
}

void ImageDownload::progress(int a, int b)
{
    qDebug(QString::number(a).toLatin1()+" : "+QString::number(b).toLatin1());
}

You can have a look at my project. It searches images on Flickr and displays the images in QListWidget. Features:

  1. Rest client for getting images list from flickr
  2. flickr api which does the job for communicating with flickr
  3. Cool looking interface
  4. Page Tracking to go to next, previous, first & last page

Link: https://github.com/hbkvikas/FlickrImageSearch It's working well but improvements can also be suggested. Screenshot for the application

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM