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 1252699831@qq.com * @net www.openedv.com * @date 2021-04-16 *******************************************************************/ 1 #ifndef MAINWINDOW_H 2 #define MAINWINDOW_H 3 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 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 1252699831@qq.com * @net www.openedv.com * @date 2021-04-16 *******************************************************************/ 1 #include "mainwindow.h" 2 #include 3 #include 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!"< 165 qDebug()<<"not JPG data, please try it again!"< 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 <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()<<"远程服务器拒绝连接"<
第 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 图片了。注意:此程序里的下载链接可能失效,请替换自己的图片链接。