11.4 网络下载实例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等功能。
11.4.1 应用实例本例目的:了解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”具体代码如下。
头文件里主要是声明界面用的元素,及一些槽函数。重点是声明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 <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行,网络响应错误处理函数。
11.4.2 程序运行效果点击下载按钮后,可以看到本次下载的图片已经保存到当前编译输出的路径下,名字叫“下载的.jpg”,并显示到界面上。由于文件下载的速度非常快,所以下载的进度条一下子就变成了100%。若想看见下载进度条下载进度缓慢一些,可以修改本例去下载其他文件,注意不要保存为jpg图片了。注意:此程序里的下载链接可能失效,请替换自己的图片链接。