QT - как скачать и сохранить изображение через http?
Я пытаюсь загрузить и сохранить некоторые изображения с помощью Qt в приложении консоли. Вот что я получил до сих пор, (весь код компилируется, но после запуска кажется, что он не входит в replyFinished()
функция...)
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...";
}
6 ответов
Загрузить изображение с помощью QNetworkAccessManager
заголовочный файл
#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
исходный файл
#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();
}
}
Ни одно из вышеперечисленных решений не помогло мне. Но я нашел эту вики-страницу Qt, которая отлично работает для всех типов данных.
Это старый вопрос, но для справки я выложу рабочую версию кода @lwinhtooko:
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;
}
Проблема оказалась в том, как QNetworkReply
использовался.
Этот вопрос старый, но у меня была похожая проблема, и теперь у меня есть решение для Qt 5.8.
Код разработан для быстрой работы, а сетевые запросы выполняются асинхронно. Поэтому вы должны предоставить идентификатор для каждого звонка, чтобы узнать, какое воспроизведение завершено.
Кроме того, этот код использует SSL. Если вы не хотите использовать шифрование SSL, удалите 4 строки с помощью 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());
}
Вы можете ознакомиться с моим проектом. Он ищет изображения на Flickr и отображает изображения в QListWidget. Функции:
- Rest клиент для получения списка изображений с flickr
- flickr api, который выполняет работу по общению с flickr
- Классный интерфейс
- Отслеживание страниц для перехода к следующей, предыдущей, первой и последней странице
Ссылка: https://github.com/hbkvikas/FlickrImageSearch Работает хорошо, но можно предложить улучшения. Скриншот для приложения
Заголовочный файл
#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
исходный файл
#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());
}