Qt 网 络 模 块 还 提 供 了 直 接 访 问 如 HTTP , FTP 等 网 络 协 议 的 类 , 这 些 类 是
QNetworkAccessManager、QNetworkRequest 和 QNetworkReply。
通常需要这三个类协作才能完成一个网络操作。可以用于从网络获取时间,天气和图片等
等数据。比如本例需要下载一张图片,大概流程如下。
由 QNetworkRequest 类设置一个 URL 地址发起网络协议请求,QNetworkRequest 类保存要
用 QNetworkAccessManager 发送的请求。QNetworkRequest 是网络访问 API 的一部分,是一个
持有通过网络发送请求所需信息的类。它包含一个 URL 和一些可用于修改请求的辅助信息。
QNetworkAccessManager 类允许应用程序发送网络请求并接收响应。在 QNetworkRequest
发起网络请求后,QNetworkAccessManager 负责发送网络请求,创建网络响应。
QNetworkReply类就用于 QNetworkAccessManager 创建的网络响应。最终由QNetworkReply
处理网络响应。它提供了 finished()、readyRead()和 downloadProgress()等信号,可以监测网络响
应的执行情况。并且 QNetworkReply 继承于 QIODevice,所以 QNetworkReply 支持流读写,可
以直接用 read()和 write 等功能。
本例目的:了解 QNetworkAccessManager、QNetworkRequest 和 QNetworkReply 类的使用。
例 12_imagedownload,下载小图片(难度:一般)。项目路径为 Qt/2/12_imagedownload。
本例大体流程,设置一个下载图片的 URL,通过 networkReply 处理响应后,从流中读取图片的
数据,然后保存到本地。
项目文件 12_imagedownload.pro 文件第一行添加的代码部分如下。
12_imagedownload.pro 编程后的代码
1 QT += core gui network
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 # Default rules for deployment.
26 qnx: target.path = /tmp/$${TARGET}/bin
27 else: unix:!android: target.path = /opt/$${TARGET}/bin
28 !isEmpty(target.path): INSTALLS += target
在头文件“mainwindow.h”具体代码如下。
mainwindow.h 编程后的代码
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 12_imagedownload
* @brief mainwindow.h
* @author Deng Zhimao
* @email [email protected]
* @net www.openedv.com
* @date 2021-04-16
*******************************************************************/
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3
4 #include <QMainWindow>
5 #include <QNetworkAccessManager>
6 #include <QNetworkReply>
7 #include <QFile>
8 #include <QLabel>
9 #include <QPushButton>
10 #include <QProgressBar>
11 #include <QHBoxLayout>
12 #include <QVBoxLayout>
13 #include <QLineEdit>
14
15 class MainWindow : public QMainWindow
16 {
17 Q_OBJECT
18
19 public:
20 MainWindow(QWidget *parent = nullptr);
21 ~MainWindow();
22 private:
23 /* 网络管理 */
24 QNetworkAccessManager *networkAccessManager;
25
26 /* 标签 */
27 QLabel *label[3];
28
29 /* 按钮 */
30 QPushButton *pushButton;
31
32 /* 下载进度条 */
33 QProgressBar *progressBar;
34
35 /* 水平布局 */
36 QHBoxLayout *hBoxLayout[2];
37
38 /* 垂直布局 */
39 QVBoxLayout *vBoxLayout;
40
41 /* 水平容器 */
42 QWidget *hWidget[2];
43
44 /* 垂直容器 */
45 QWidget *vWidget;
46
47 /* 链接输入框 */
48 QLineEdit *lineEdit;
49
50 private slots:
51 /* 读取数据 */
52 void readyReadData();
53
54 /* 响应完成处理 */
55 void replyFinished();
56
57 /* 下载进度管理 */
58 void imageDownloadProgress(qint64, qint64);
59
60 /* 点击开始下载 */
61 void startDownload();
62
63 /* 响应错误处理函数 */
64 void networkReplyError(QNetworkReply::NetworkError);
65 };
66 #endif // MAINWINDOW_H
头文件里主要是声明界面用的元素,及一些槽函数。重点是声明 networkAccessManager。
在源文件“mainwindow.cpp”具体代码如下。
mainwindow.cpp 编程后的代码
/******************************************************************
Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
* @projectName 12_imagedownload
* * @brief mainwindow.cpp
* @author Deng Zhimao
* @email [email protected]
* @net www.openedv.com
* @date 2021-04-16
*******************************************************************/
1 #include "mainwindow.h"
2 #include <QMessageBox>
3 #include <QCoreApplication>
4
5 MainWindow::MainWindow(QWidget *parent)
6 : QMainWindow(parent)
7 {
8 /* 设置主窗体的位置与大小 */
9 this->setGeometry(0, 0, 800, 480);
10
11 /* 标签 0, 显示下载的图像 */
12 label[0] = new QLabel();
13 /* 标签 1, 显示 URL 标签 */
14 label[1] = new QLabel();
15 /* 下载进度标签 */
16 label[2] = new QLabel();
17
18 /* 下载图片链接输入框 */
19 lineEdit = new QLineEdit();
20
21 /* 下载按钮 */
22 pushButton = new QPushButton();
23
24 /* 下载进度条 */
25 progressBar = new QProgressBar();
26
27 /* 水平布局 */
28 hBoxLayout[0] = new QHBoxLayout();
29 hBoxLayout[1] = new QHBoxLayout();
30
31 /* 垂直布局 */
32 vBoxLayout = new QVBoxLayout();
33
34 /* 水平容器 */
35 hWidget[0] = new QWidget();
36 hWidget[1] = new QWidget();
37
38 /* 垂直容器 */
39 vWidget = new QWidget();
40
41 label[1]->setText("URL 链接:");
42 label[2]->setText("文件下载进度:");
43
44 pushButton->setText("下载");
45
46 /* 设置下载链接地址 */
47 lineEdit->setText("https://ss0.bdstatic.com/70cFuH"
48 "Sh_Q1YnxGkpoWK1HF6hhy/it/u=42710"
49 "87328,1384669424&fm=11&gp=0.jpg");
50 /* 设置标签的最小显示大小 */
51 label[0]->setMinimumSize(this->width(),
52 this->height() * 0.75);
53
54 /* 根据文本文字大小自适应大小 */
55 label[1]->setSizePolicy(QSizePolicy::Fixed,
56 QSizePolicy::Fixed);
57 label[2]->setSizePolicy(QSizePolicy::Fixed,
58 QSizePolicy::Fixed);
59 pushButton->setSizePolicy(QSizePolicy::Fixed,
60 QSizePolicy::Fixed);
61
62 /* 水平布局 0 添加元素 */
63 hBoxLayout[0]->addWidget(label[1]);
64 hBoxLayout[0]->addWidget(lineEdit);
65 hBoxLayout[0]->addWidget(pushButton);
66
67 /* 设置水平布局 0 为水平容器的布局 0 */
68 hWidget[0]->setLayout(hBoxLayout[0]);
69
70 /* 水平布局 1 添加元素 */
71 hBoxLayout[1]->addWidget(label[2]);
72 hBoxLayout[1]->addWidget(progressBar);
73
74 /* 设置水平布局 1 为水平容器的布局 1 */
75 hWidget[1]->setLayout(hBoxLayout[1]);
76
77 /* 垂直布局添加元素 */
78 vBoxLayout->addWidget(label[0]);
79 vBoxLayout->addWidget(hWidget[0]);
80 vBoxLayout->addWidget(hWidget[1]);
81
82 /* 设置垂直布局为垂直容器的布局 */
83 vWidget->setLayout(vBoxLayout);
84
85 /* 设置居中 */
86 setCentralWidget(vWidget);
87
88 /* 网络管理 */
89 networkAccessManager = new QNetworkAccessManager(this);
90
91 /* 信号槽连接 */
92 connect(pushButton, SIGNAL(clicked()),
93 this, SLOT(startDownload()));
94
95 }
96
97 MainWindow::~MainWindow()
98 {
99 }
100
101 void MainWindow::startDownload()
102 {
103 /* 获取 URL 链接 */
104 QUrl newUrl(QUrl(lineEdit->text()));
105
106 /* 如果下载链接无效,则直接返回 */
107 if (!newUrl.isValid()) {
108 QMessageBox::information(this, "error", "invalid url");
109 return;
110 }
111
112 /* 网络请求 */
113 QNetworkRequest networkRequest;
114
115 /* 设置下载的地址 */
116 networkRequest.setUrl(newUrl);
117
118 /* 网络响应 */
119 QNetworkReply *newReply =
120 networkAccessManager->get(networkRequest);
121
122 /* 信号槽连接 */
123 connect(newReply, SIGNAL(finished()),
124 this, SLOT(replyFinished()));
125 connect(newReply, SIGNAL(readyRead()),
126 this, SLOT(readyReadData()));
127 connect(newReply, SIGNAL(downloadProgress(qint64, qint64)),
128 this, SLOT(imageDownloadProgress(qint64, qint64)));
129 connect(newReply,
130 SIGNAL(error(QNetworkReply::NetworkError)),
131 this,
132 SLOT(networkReplyError(QNetworkReply::NetworkError )));
133 }
134
135 void MainWindow::readyReadData()
136 {
137 /* 设置按钮不可用,防止未完成,再次点击 */
138 pushButton->setEnabled(false);
139
140 /* 获取信号发送者 */
141 QNetworkReply *reply = (QNetworkReply *)sender();
142
143 QFile imageFile;
144 /* 保存到当前路径,名称为"下载的.jpg" */
145 imageFile.setFileName(QCoreApplication::applicationDirPath()
146 + "/下载的.jpg");
147
148 /* 如果此图片已经存在,则删除 */
149 if (imageFile.exists())
150 imageFile.remove();
151
152 /* 读取数据 */
153 QByteArray data = reply->readAll();
154 /* 如果数据为空,返回 */
155 if (data.isEmpty()) {
156 qDebug()<<"data is null, please try it again!"<<endl;
157 return;
158 }
159
160 /* 判断是不是 JPG 格式的图片,如果不是则返回 */
161 if (! (data[0] == (char)0xff
162 && data[1] == (char)0xd8
163 && data[data.size() - 2] == (char)0xff
164 && data[data.size() - 1] == (char)0xd9)) {
165 qDebug()<<"not JPG data, please try it again!"<<endl;
166 return;
167 }
168
169 /* 转为 QPixmap */
170 QPixmap pixmap;
171 pixmap.loadFromData(data);
172 pixmap.save(imageFile.fileName());
173 }
174
175 void MainWindow::replyFinished()
176 {
177 /* 获取信号发送者 */
178 QNetworkReply *reply = (QNetworkReply *)sender();
179
180 /* 防止内存泄漏 */
181 reply->deleteLater();
182
183 /* 判断当前执行程序下的图像是否下载完成 */
184 QFile imageFile(QCoreApplication::applicationDirPath()
185 + "/下载的.jpg");
186 if (imageFile.exists()) {
187 /* 显示下载的图像 */
188 label[0]->setPixmap(QPixmap(imageFile.fileName()));
189 qDebug() <<"已经成功下载,文件路径为:"
190 <<imageFile.fileName()<<endl;
191 } else
192 /* 清空显示 */
193 label[0]->clear();
194
195 /* 设置按钮可用 */
196 pushButton->setEnabled(true);
197 }
198
199 void MainWindow::imageDownloadProgress(qint64 bytes,
200 qint64 totalBytes)
201 {
202 /* 设置进度条的最大值 */
203 progressBar->setMaximum(totalBytes);
204 /* 设置当前值 */
205 progressBar->setValue(bytes);
206 }
207
208 /* 网络响应处理函数 */
209 void MainWindow::networkReplyError(QNetworkReply::NetworkError
210 error)
211 {
212 switch (error) {
213 case QNetworkReply::ConnectionRefusedError:
214 qDebug()<<"远程服务器拒绝连接"<<endl;
215 break;
216 case QNetworkReply::HostNotFoundError:
217 qDebug()<<"找不到远程主机名"<<endl;
218 break;
219 case QNetworkReply::TimeoutError:
220 qDebug()<<"与远程服务器连接超时"<<endl;
221 break;
222 default:
223 break;
224 }
225 }
第 89 行,全局变量 networkAccessManager 实例化。
第 101 行~133 行,首先从单行输入框里获取 URL 链接为 newUrl,判断链接的有效性。然
后创建局部变量 networkRequest,设置 networkRequest 请求 URL 为 newUrl。QNetworkReply
*newReply = networkAccessManager->get(networkRequest);为最重要的代码,所有响应本次的操
作都交给了 newReply。通过信号槽处理对应的操作。
第 135~173 行,这部分代码就是从 newReply 流里读取网络下载的数据。
第 160~167 行,这里笔者做了一些处理,从网络下载的数据可能遇到数据丢失或者下载错
误的情况。本例是从百度里下载一张 JPG 格式的图片,因为 JPG 图片的判断依据是第一个字节
和第二个字节的数据是 0xff 和 0xd8,倒数第一个和倒数第二个字节数据分别是 0xd9 和 0xff。
如果都对,那么判断此数据为 JPG 图片数据。然后进行保存,否则则不保存图片。处理数据往
往是需要的,我们经常要对下载的数据进行处理。
第 174~197 行,网络响应完成。记得要删除 reply,防止内存泄漏。如果下载成功图片,则
显示图片到 label[0]上。
第 209~225 行,网络响应错误处理函数。
点击下载按钮后,可以看到本次下载的图片已经保存到当前编译输出的路径下,名字叫“下
载的.jpg”,并显示到界面上。由于文件下载的速度非常快,所以下载的进度条一下子就变成了
100%。若想看见下载进度条下载进度缓慢一些,可以修改本例去下载其他文件,注意不要保存
为 jpg 图片了。注意:此程序里的下载链接可能失效,请替换自己的图片链接。