gemini:
在 Qt 的网络模块中,如果说 QTcpSocket 是让你亲自去铺设电缆、处理字节流的"底层苦工",那么 QNetworkAccessManager 及其相关类就是一套高级的现代化物流系统。
为了让你生动形象地理解这"网络三剑客",我们可以把网络请求的过程比作一次 "快递寄送与签收" 的全过程。
📦 生动形象的"物流系统"比喻
想象一下,你坐在家里(你的应用程序),想要从远方的仓库(服务器)获取一件商品(下载数据)
1. QNetworkAccessManager ------ "物流调度总站"
-
角色 :这是整个系统的核心大脑 ,也就是调度员。
-
作用:你不需要亲自去跑腿,也不需要关心路怎么走(是 HTTP 还是 FTP,是否有代理)。你只需要把你的需求告诉这位"调度员",它会负责指派卡车、安排路线、处理并发,并最终通知你"货到了"。
-
特点 :通常一个应用程序只需要一个这样的调度员(单例模式使用最常见)。
2. QNetworkRequest ------ "运单/需求单"
-
角色 :这是你填写的寄件单。
-
作用:这张单子上写着:
-
地址 (URL) :货在哪个仓库?(例如
http://www.google.com) -
备注 (Headers) :这批货是什么类型的?我是谁?(例如
User-Agent,Content-Type)。
-
-
特点:它只是一个信息的载体,本身不干活,只负责承载信息给调度员看。
3. QNetworkReply ------ "包裹/回执"
-
角色 :这是调度员派出去处理完任务后,给你的反馈对象。
-
作用 :注意,它不仅包含包裹里的东西 (服务器返回的数据),还包含物流状态(状态码 200 OK 还是 404 Not Found)。
-
特点 :它是一个 I/O 设备(继承自
QIODevice),你可以像读取文件一样读取它。同时,它会实时告诉你进度(downloadProgress信号),就像物流信息的实时更新。
🛠️ 详细拆解与分工
1. QNetworkAccessManager (QNAM)
它是发起网络请求的入口。
-
关键方法:
-
get(request): 发起一个下载请求(GET)。 -
post(request, data): 发起一个上传/提交请求(POST)。
-
-
关键信号:
finished(QNetworkReply*): 只要有任何请求结束了(无论成功失败),这个大喇叭就会广播。
2. QNetworkRequest
它是请求的配置容器。
-
设置地址 :
setUrl(QUrl("https://...")) -
设置头信息 :
setRawHeader("Content-Type", "application/json")
3. QNetworkReply
它是请求的结果,也是数据流。
-
获取数据 :
readAll()(因为它是 QIODevice)。 -
检查错误 :
error()和errorString()。 -
获取响应头 :
rawHeader()。 -
生命周期 :极其重要! 它是动态创建的对象,用完必须由你(或者设置自动删除)来清理,否则会内存泄漏。
💻 简单的使用方法 (代码示例)
下面是一个经典的HTTP GET 下载示例。我们将创建一个简单的类来演示如何下载一个网页的 HTML 内容。
场景:下载百度首页的 HTML
cpp
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
#include <QObject>
class Downloader : public QObject {
Q_OBJECT
public:
Downloader(QObject *parent = nullptr) : QObject(parent) {
// 1. 创建"物流调度员" (QNetworkAccessManager)
manager = new QNetworkAccessManager(this);
// 连接信号:当请求完成时,调用我们的槽函数
connect(manager, &QNetworkAccessManager::finished,
this, &Downloader::onResult);
}
void doDownload() {
// 2. 填写"运单" (QNetworkRequest)
QUrl url("http://www.baidu.com");
QNetworkRequest request(url);
// 伪装成浏览器(可选,防止被某些服务器拒绝)
request.setRawHeader("User-Agent", "MyQtApp 1.0");
qDebug() << "开始请求:" << url.toString();
// 3. 交给调度员,并拿到"包裹凭证" (QNetworkReply)
// 注意:这里是异步的!代码会立刻往下执行,不会卡在这里等下载。
manager->get(request);
}
public slots:
// 4. 处理"到货" (Slot)
void onResult(QNetworkReply *reply) {
// 检查是否有错误
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "下载出错:" << reply->errorString();
} else {
// 获取 HTTP 状态码 (例如 200)
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << "状态码:" << statusCode;
// 5. 拆包裹:读取所有数据
QByteArray data = reply->readAll();
qDebug() << "下载内容大小:" << data.size() << "bytes";
// qDebug() << "内容预览:" << data.left(100);
}
// 6. 清理垃圾! (非常重要)
// reply 是 new 出来的,需要释放。
// 使用 deleteLater() 比 delete 更安全,因为它会等待当前事件循环结束。
reply->deleteLater();
// 如果是控制台程序,可以在这里退出
// QCoreApplication::quit();
}
private:
QNetworkAccessManager *manager;
};
// 配合 main.cpp 使用时,只需实例化 Downloader 并调用 doDownload()
⚠️ 核心注意事项 (避坑指南)
-
异步即正义:
Qt 的网络模块是异步的(Asynchronous)。当你调用 manager->get() 时,函数会立刻返回,界面不会卡死。数据还没回来呢!你必须依赖 finished 信号或者 readyRead 信号来知道数据什么时候到了。
不要试图写
while(!reply->isFinished())这样的死循环去等数据,那会让你的 GUI 失去响应! -
对象的所属权:
QNetworkReply 对象是由 QNetworkAccessManager 创建并返回给你的,它的生杀大权在你手里。Qt 不会自动帮你 delete 它。一定要在用完后调用 reply->deleteLater(),否则随着请求增多,内存会爆涨。
-
HTTPS 支持:
如果你访问 https:// 开头的网址,Qt 需要 OpenSSL 库的支持。如果在 Windows 上运行报错(TLS/SSL 错误),通常是因为缺少 libssl 和 libcrypto 相关的 DLL 文件。
-
大文件下载:
上面的例子用了 readAll(),这适合小文件(文本、图片)。如果要下载几百兆的大文件,不要在 finished 里一次性读完(内存会爆)。
正确做法:连接 QNetworkReply::readyRead 信号,每收到一点数据就写入硬盘文件,然后清空内存缓冲区。
/******************************************************************************************************/
常用函数:
为了让你快速上手,我们把这三个类中最常用的"高频函数"挑出来。记住我们的比喻:调度员 (Manager) 、运单 (Request) 和 包裹/回执 (Reply)。
以下是按使用频率排序的核心函数清单:
1. QNetworkAccessManager (调度员)
核心任务:发起请求。你最常调用的就是那几个动词。
| 函数名 | 作用 (通俗解释) | 典型场景 |
|---|---|---|
get(const QNetworkRequest &request) |
发起下载。告诉调度员:"去把这个东西取回来"。 | 下载网页 HTML、图片、JSON 数据。 |
post(const QNetworkRequest &request, const QByteArray &data) |
发起上传。告诉调度员:"把这些数据送到服务器去"。 | 登录表单提交、上传文件、发送 JSON 指令。 |
finished(QNetworkReply *reply) |
(信号) 任务结束大喇叭。无论成功失败,只要这趟活干完了,就发信号。 | 在这里统一处理结果、断开连接。 |
2. QNetworkRequest (运单)
核心任务:配置请求的细节。在交给调度员之前,你得把单子填好。
| 函数名 | 作用 (通俗解释) | 典型代码 |
|---|---|---|
setUrl(const QUrl &url) |
写地址。这是必填项,没有地址没法送。 | req.setUrl(QUrl("http://...")); |
setRawHeader(const QByteArray &name, const QByteArray &value) |
贴自定义标签。最通用的设置头信息的方法。 | 设置 User-Agent (伪装浏览器)、Token (身份验证)。 req.setRawHeader("User-Agent", "Chrome"); |
setHeader(KnownHeaders header, const QVariant &value) |
贴标准标签。Qt 预定义了一些常用头,用枚举值设置,防止你拼写错误。 | req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); |
3. QNetworkReply (包裹/回执)
核心任务:处理结果。这是功能最丰富的一个类,因为它既包含数据,也包含状态。
A. 获取数据 (拆包裹)
由于它继承自 QIODevice,文件操作函数它都能用:
-
readAll():一锅端。一次性读取所有下载的数据。适合小文件(文本、API 响应)。 -
read(qint64 maxSize):一点点读。配合流式写入文件时使用。
B. 检查状态 (看回执)
-
error():有没有出错? 返回错误枚举值(如NoError,ConnectionRefusedError)。 -
errorString():错误详情。返回人类可读的错误描述字符串。 -
attribute(QNetworkRequest::Attribute code):查底单 。最常用的是获取 HTTP 状态码。- 常用写法 :
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();(用来判断是 200 还是 404)。
- 常用写法 :
C. 监控进度 (物流追踪)
-
downloadProgress(qint64 bytesReceived, qint64 bytesTotal):(信号) 下载进度更新。常用来更新进度条(UI)。 -
uploadProgress(qint64 bytesSent, qint64 bytesTotal):(信号) 上传进度更新。
D. 生命周期管理 (扔包装)
-
deleteLater():销毁对象 。千万别直接delete reply,因为可能信号还没发完。用这个函数告诉 Qt:"等当前没事了,把它清理掉"。 -
abort():拒收/取消。强行中断当前的下载或上传。
⚡️ 常用"连招" (代码片段)
场景 1:最常用的 Header 设置 (JSON 通信)
cpp
QNetworkRequest request(QUrl("http://api.example.com/data"));
// 告诉服务器我要发JSON过去
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
// 告诉服务器我希望收JSON回来
request.setRawHeader("Accept", "application/json");
场景 2:检查 HTTP 状态码 (防坑)
很多时候网络是通的 (NoError),但服务器报错了 (比如 500 服务器错误)。所以既要看 error() 也要看状态码。
cpp
// 在 finished 槽函数中
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (statusCode.isValid() && statusCode.toInt() == 200) {
// 真正成功
}
场景 3:实现进度条
cpp
// 假设你有一个 QProgressBar *bar
connect(reply, &QNetworkReply::downloadProgress, this, [=](qint64 now, qint64 total){
if (total > 0) {
bar->setMaximum(total);
bar->setValue(now);
}
});
gpt:
当然可以!我来给你一个生动形象、详细的介绍 ,让你理解 Qt 网络模块中 HTTP/FTP 下载相关的三个核心类:QNetworkAccessManager、QNetworkRequest 和 QNetworkReply,并附上简单的使用示例。
1️⃣ 概念与类介绍
Qt 的网络模块可以让我们像"点对点邮递员"一样访问互联网资源。核心的"邮递链条"分为三环:
1. QNetworkAccessManager --- 网络经理
-
比喻:像一个万能的快递公司,你告诉它"我要寄这个包裹(请求)",它负责把包裹送到目的地并把回信带回来。
-
功能:
-
发送各种网络请求(HTTP GET、POST、FTP 等)。
-
管理请求和应答。
-
处理异步操作(Qt 网络默认是异步的)。
-
-
注意 :通常整个应用程序中可以共用一个
QNetworkAccessManager实例,因为它内部会管理连接池和缓存。
2. QNetworkRequest --- 网络请求
-
比喻:你写给快递公司的"快递单",里面有:
-
地址(URL)
-
包裹类型(请求头,比如 Content-Type)
-
其他信息(Cookie、User-Agent 等)
-
-
功能:
-
封装请求目标(URL)。
-
设置请求头(如请求格式、认证信息)。
-
-
常用方法:
-
setUrl(const QUrl &url):设置请求地址。 -
setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value):设置 HTTP 请求头。
-
3. QNetworkReply --- 网络回复
-
比喻:快递公司给你送回来的"回信",里面有:
-
返回数据(文件内容、网页源码)
-
状态码(200 OK、404 Not Found)
-
错误信息(如果请求失败)
-
-
功能:
-
异步返回服务器响应。
-
可以通过信号接收数据,如:
-
readyRead():有数据可读。 -
finished():请求完成。 -
errorOccurred():请求出错。
-
-
-
注意 :请求完成后,需要手动删除
QNetworkReply(或者使用deleteLater())。
2️⃣ 网络下载示例
下面我们用 Qt 下载一个网页内容作为演示(异步操作):
cpp
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 1. 创建网络经理(全局可用)
QNetworkAccessManager manager;
// 2. 创建请求(写快递单)
QUrl url("https://www.example.com");
QNetworkRequest request(url);
// 3. 发送 GET 请求
QNetworkReply *reply = manager.get(request);
// 4. 连接信号槽处理异步下载
QObject::connect(reply, &QNetworkReply::finished, [&]() {
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll(); // 获取返回内容
qDebug() << "下载完成,内容长度:" << data.size();
qDebug() << data.left(200); // 打印前200字节
} else {
qDebug() << "下载出错:" << reply->errorString();
}
reply->deleteLater(); // 清理
QCoreApplication::quit();
});
return a.exec();
}
🔹 运行原理
-
manager.get(request)发起 GET 请求。 -
Qt 内部使用事件循环异步处理网络请求。
-
请求完成后,
finished()信号触发。 -
通过
reply->readAll()获取数据。 -
最后调用
deleteLater()清理对象。
3️⃣ 小结
| 类名 | 功能 | 比喻 |
|---|---|---|
QNetworkAccessManager |
发送请求,管理网络事务 | 快递公司 |
QNetworkRequest |
封装请求信息 | 快递单 |
QNetworkReply |
接收服务器响应 | 回信 |
使用步骤:
-
创建
QNetworkAccessManager。 -
创建并配置
QNetworkRequest(URL + 请求头)。 -
调用
manager.get(request)或post()发起请求。 -
通过
QNetworkReply信号处理返回数据。 -
完成后清理
QNetworkReply。
/***********************************************************************************************************/
常用函数:
明白了,你想了解 Qt 网络模块中 常用的函数 ,尤其是用于 上传和下载文件或数据 的。我们可以针对 QNetworkAccessManager、QNetworkRequest 和 QNetworkReply 来整理一张"速查表",并举例说明。
1️⃣ QNetworkAccessManager --- 网络操作核心
| 功能 | 常用函数 | 说明 |
|---|---|---|
| 发起 GET 请求 | QNetworkReply *get(const QNetworkRequest &request) |
下载数据或文件 |
| 发起 POST 请求 | QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data) |
上传数据,如表单或 JSON |
| 发起 PUT 请求 | QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data) |
上传完整文件或内容 |
| 发起 DELETE 请求 | QNetworkReply *deleteResource(const QNetworkRequest &request) |
删除服务器资源 |
| 发起自定义请求 | QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data = QByteArray()) |
支持任意 HTTP 方法 |
注意 :返回的都是 QNetworkReply*,操作是异步的,需要通过信号处理。
2️⃣ QNetworkRequest --- 请求配置
| 功能 | 常用函数 | 说明 |
|---|---|---|
| 设置请求 URL | void setUrl(const QUrl &url) |
必须指定下载或上传目标地址 |
| 设置请求头 | void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value) |
如 ContentTypeHeader, ContentLengthHeader |
| 设置自定义原始头 | void setRawHeader(const QByteArray &headerName, const QByteArray &value) |
例如自定义 Authorization 或 User-Agent |
| 设置超时 / 缓存策略 | void setAttribute(QNetworkRequest::Attribute code, const QVariant &value) |
如 CacheLoadControlAttribute |
3️⃣ QNetworkReply --- 响应处理
| 功能 | 常用函数/信号 | 说明 |
|---|---|---|
| 读取数据 | QByteArray readAll() |
获取服务器返回的数据 |
| 监听数据可读 | readyRead() 信号 |
数据可以分段读取 |
| 请求完成 | finished() 信号 |
数据下载完成 |
| 获取 HTTP 状态码 | int attribute(QNetworkRequest::HttpStatusCodeAttribute) |
如 200、404 |
| 获取错误 | QNetworkReply::NetworkError error() |
判断请求是否成功 |
| 上传进度 | uploadProgress(qint64 bytesSent, qint64 bytesTotal) 信号 |
POST/PUT 上传进度 |
| 下载进度 | downloadProgress(qint64 bytesReceived, qint64 bytesTotal) 信号 |
GET 下载进度 |
4️⃣ 下载文件示例(带进度)
cpp
QNetworkAccessManager manager;
QUrl url("https://www.example.com/file.zip");
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
// 下载进度
QObject::connect(reply, &QNetworkReply::downloadProgress, [](qint64 bytesReceived, qint64 bytesTotal){
qDebug() << "下载进度:" << bytesReceived << "/" << bytesTotal;
});
QObject::connect(reply, &QNetworkReply::finished, [reply](){
if(reply->error() == QNetworkReply::NoError){
QFile file("file.zip");
if(file.open(QIODevice::WriteOnly))
file.write(reply->readAll());
file.close();
qDebug() << "下载完成";
} else {
qDebug() << "下载失败:" << reply->errorString();
}
reply->deleteLater();
});
5️⃣ 上传文件示例(POST)
cpp
QNetworkAccessManager manager;
QUrl url("https://www.example.com/upload");
QNetworkRequest request(url);
// 设置 Content-Type
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
// 读取本地文件
QFile file("file_to_upload.bin");
file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
file.close();
// 上传
QNetworkReply *reply = manager.post(request, data);
// 上传进度
QObject::connect(reply, &QNetworkReply::uploadProgress, [](qint64 bytesSent, qint64 bytesTotal){
qDebug() << "上传进度:" << bytesSent << "/" << bytesTotal;
});
// 上传完成
QObject::connect(reply, &QNetworkReply::finished, [reply](){
if(reply->error() == QNetworkReply::NoError)
qDebug() << "上传完成";
else
qDebug() << "上传失败:" << reply->errorString();
reply->deleteLater();
});
✅ 小结
-
下载文件 →
QNetworkAccessManager::get()+QNetworkReply::downloadProgress() -
上传文件 →
QNetworkAccessManager::post()/put()+QNetworkReply::uploadProgress() -
请求头设置 →
QNetworkRequest::setHeader()/setRawHeader() -
读取数据 →
QNetworkReply::readAll()或分段读取 -
状态与错误检查 →
QNetworkReply::error()+finished()
项目教程代码:
cpp
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 12_imagedownload
* @brief mainwindow.h
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-16
*******************************************************************/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QFile>
#include <QLabel>
#include <QPushButton>
#include <QProgressBar>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLineEdit>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
/* 网络管理 */
QNetworkAccessManager *networkAccessManager;
/* 标签 */
QLabel *label[3];
/* 按钮 */
QPushButton *pushButton;
/* 下载进度条 */
QProgressBar *progressBar;
/* 水平布局 */
QHBoxLayout *hBoxLayout[2];
/* 垂直布局 */
QVBoxLayout *vBoxLayout;
/* 水平容器 */
QWidget *hWidget[2];
/* 垂直容器 */
QWidget *vWidget;
/* 链接输入框 */
QLineEdit *lineEdit;
private slots:
/* 读取数据 */
void readyReadData();
/* 读取数据 */
void readyReadData(QNetworkReply*);
/* 响应完成处理 */
void replyFinished();
/* 下载进度管理 */
void imageDownloadProgress(qint64, qint64);
/* 点击开始下载 */
void startDownload();
/* 响应错误处理函数 */
void networkReplyError(QNetworkReply::NetworkError);
};
#endif // MAINWINDOW_H
cpp
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 12_imagedownload
* @brief mainwindow.cpp
* @author Deng Zhimao
* @email 1252699831@qq.com
* @net www.openedv.com
* @date 2021-04-16
*******************************************************************/
#include "mainwindow.h"
#include <QMessageBox>
#include <QCoreApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
/* 设置主窗体的位置与大小 */
this->setGeometry(0, 0, 800, 480);
/* 标签0, 显示下载的图像 */
label[0] = new QLabel();
/* 标签1, 显示URL标签 */
label[1] = new QLabel();
/* 下载进度标签 */
label[2] = new QLabel();
/* 下载图片链接输入框 */
lineEdit = new QLineEdit();
/* 下载按钮 */
pushButton = new QPushButton();
/* 下载进度条 */
progressBar = new QProgressBar();
/* 水平布局 */
hBoxLayout[0] = new QHBoxLayout();
hBoxLayout[1] = new QHBoxLayout();
/* 垂直布局 */
vBoxLayout = new QVBoxLayout();
/* 水平容器 */
hWidget[0] = new QWidget();
hWidget[1] = new QWidget();
/* 垂直容器 */
vWidget = new QWidget();
label[1]->setText("URL链接:");
label[2]->setText("文件下载进度:");
pushButton->setText("下载");
/* 设置下载链接地址,在浏览器右键复制图片下载地址即可 */
lineEdit->setText("https://img1.baidu.com/it/u=3908406816,2850982836&"
"fm=253&fmt=auto&app=120&f=JPEG?w=889&h=500");
/* 设置标签的最小显示大小 */
label[0]->setMinimumSize(this->width(),
this->height() * 0.75);
/* 根据文本文字大小自适应大小 */
label[1]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
label[2]->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
pushButton->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
/* 水平布局0添加元素 */
hBoxLayout[0]->addWidget(label[1]);
hBoxLayout[0]->addWidget(lineEdit);
hBoxLayout[0]->addWidget(pushButton);
/* 设置水平布局0为水平容器的布局0 */
hWidget[0]->setLayout(hBoxLayout[0]);
/* 水平布局1添加元素 */
hBoxLayout[1]->addWidget(label[2]);
hBoxLayout[1]->addWidget(progressBar);
/* 设置水平布局1为水平容器的布局1 */
hWidget[1]->setLayout(hBoxLayout[1]);
/* 垂直布局添加元素 */
vBoxLayout->addWidget(label[0]);
vBoxLayout->addWidget(hWidget[0]);
vBoxLayout->addWidget(hWidget[1]);
/* 设置垂直布局为垂直容器的布局 */
vWidget->setLayout(vBoxLayout);
/* 设置居中 */
setCentralWidget(vWidget);
/* 网络管理 */
networkAccessManager = new QNetworkAccessManager(this);
/* 信号槽连接 */
connect(pushButton, SIGNAL(clicked()),
this, SLOT(startDownload()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::startDownload()
{
/* 获取URL链接 */
QUrl newUrl(QUrl(lineEdit->text()));
/* 如果下载链接无效,则直接返回 */
if (!newUrl.isValid()) {
QMessageBox::information(this, "error", "invalid url");
return;
}
/* 网络请求 */
QNetworkRequest networkRequest;
/* 设置下载的地址 */
networkRequest.setUrl(newUrl);
/* 网络响应 */
QNetworkReply *newReply =
networkAccessManager->get(networkRequest);
/* 信号槽连接 */
connect(newReply, SIGNAL(finished()),
this, SLOT(replyFinished()));
/* 请求成功后,立刻去读,才能成功下载图片 */
// connect(newReply, SIGNAL(readyRead()),
// this, SLOT(readyReadData()));
connect(newReply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(imageDownloadProgress(qint64, qint64)));
connect(newReply,
SIGNAL(error(QNetworkReply::NetworkError)),
this,
SLOT(networkReplyError(QNetworkReply::NetworkError )));
}
void MainWindow::readyReadData()
{
/* 设置按钮不可用,防止未完成,再次点击 */
pushButton->setEnabled(false);
/* 获取信号发送者 */
QNetworkReply *reply = (QNetworkReply *)sender();
QFile imageFile;
/* 保存到当前路径,名称为"下载的.jpg" */
imageFile.setFileName(QCoreApplication::applicationDirPath()
+ "/下载的.jpg");
/* 如果此图片已经存在,则删除 */
if (imageFile.exists())
imageFile.remove();
/* 读取数据 */
QByteArray data = reply->readAll();
/* 如果数据为空,返回 */
if (data.isEmpty()) {
qDebug()<<"data is null, please try it again!"<<endl;
return;
}
/* 判断是不是JPG格式的图片,如果不是则返回,如果不需要判断JPG可以删除这部分代码 */
if (! (data[0] == (char)0xff
&& data[1] == (char)0xd8
&& data[data.size() - 2] == (char)0xff
&& data[data.size() - 1] == (char)0xd9)) {
qDebug()<<"not JPG data, please try it again!"<<endl;
return;
}
/* 转为QPixmap */
QPixmap pixmap;
pixmap.loadFromData(data);
pixmap.save(imageFile.fileName());
}
void MainWindow::readyReadData(QNetworkReply *reply)
{
/* 设置按钮不可用,防止未完成,再次点击 */
pushButton->setEnabled(false);
QFile imageFile;
/* 保存到当前路径,名称为"下载的.jpg" */
imageFile.setFileName(QCoreApplication::applicationDirPath()
+ "/下载的.jpg");
/* 如果此图片已经存在,则删除 */
if (imageFile.exists())
imageFile.remove();
/* 读取数据 */
QByteArray data = reply->readAll();
/* 如果数据为空,返回 */
if (data.isEmpty()) {
qDebug()<<"data is null, please try it again!"<<endl;
return;
}
/* 判断是不是JPG格式的图片,如果不是则返回 */
if (! (data[0] == (char)0xff
&& data[1] == (char)0xd8
&& data[data.size() - 2] == (char)0xff
&& data[data.size() - 1] == (char)0xd9)) {
qDebug()<<"not JPG data, please try it again!"<<endl;
return;
}
/* 转为QPixmap */
QPixmap pixmap;
pixmap.loadFromData(data);
pixmap.save(imageFile.fileName());
}
void MainWindow::replyFinished()
{
/* 获取信号发送者 */
QNetworkReply *reply = (QNetworkReply *)sender();
/* 防止内存泄漏 */
reply->deleteLater();
/* 立刻读取数据 */
readyReadData(reply);
/* 判断当前执行程序下的图像是否下载完成 */
QFile imageFile(QCoreApplication::applicationDirPath()
+ "/下载的.jpg");
if (imageFile.exists()) {
/* 显示下载的图像 */
label[0]->setPixmap(QPixmap(imageFile.fileName()));
qDebug() <<"已经成功下载,文件路径为:"
<<imageFile.fileName()<<endl;
} else
/* 清空显示 */
label[0]->clear();
/* 设置按钮可用 */
pushButton->setEnabled(true);
}
void MainWindow::imageDownloadProgress(qint64 bytes,
qint64 totalBytes)
{
/* 设置进度条的最大值 */
progressBar->setMaximum(totalBytes);
/* 设置当前值 */
progressBar->setValue(bytes);
}
/* 网络响应处理函数 */
void MainWindow::networkReplyError(QNetworkReply::NetworkError
error)
{
switch (error) {
case QNetworkReply::ConnectionRefusedError:
qDebug()<<"远程服务器拒绝连接"<<endl;
break;
case QNetworkReply::HostNotFoundError:
qDebug()<<"找不到远程主机名"<<endl;
break;
case QNetworkReply::TimeoutError:
qDebug()<<"与远程服务器连接超时"<<endl;
break;
default:
break;
}
}
