OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3020|回复: 0

《I.MX6U 嵌入式Qt开发指南》第十一章 网络编程 11.4 网络下载实例

[复制链接]

1130

主题

1141

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4746
金钱
4746
注册时间
2019-5-8
在线时间
1237 小时
发表于 2022-8-9 17:27:47 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2022-8-10 17:32 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)  章节摘自【正点原子】《I.MX6U 嵌入式Qt开发指南》

3)购买链接:https://detail.tmall.com/item.htm?id=609033604451
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子阿尔法Linux交流群:1027879335




第十一章 网络编程
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文件第一行添加的代码部分如下。

  1. 12_imagedownload.pro编程后的代码

  2. 1   QT       += core gui network

  3. 2

  4. 3   greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

  5. 4

  6. 5   CONFIG += c++11

  7. 6

  8. 7   # The following define makes your compiler emit warnings if you use

  9. 8   # any Qt feature that has been marked deprecated (the exact warnings

  10. 9   # depend on your compiler). Please consult the documentation of the

  11. 10  # deprecated API in order to know how to port your code away from it.

  12. 11  DEFINES += QT_DEPRECATED_WARNINGS

  13. 12

  14. 13  # You can also make your code fail to compile if it uses deprecated APIs.

  15. 14  # In order to do so, uncomment the following line.

  16. 15  # You can also select to disable deprecated APIs only up to a certain version of Qt.

  17. 16  #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

  18. 17

  19. 18  SOURCES += \

  20. 19      main.cpp \

  21. 20      mainwindow.cpp

  22. 21

  23. 22  HEADERS += \

  24. 23      mainwindow.h

  25. 24

  26. 25  # Default rules for deployment.

  27. 26  qnx: target.path = /tmp/${TARGET}/bin

  28. 27  else: unix:!android: target.path = /opt/${TARGET}/bin

  29. 28  !isEmpty(target.path): INSTALLS += target
复制代码
在头文件“mainwindow.h”具体代码如下。
  1. mainwindow.h编程后的代码

  2.     /******************************************************************

  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.

  4.     * @projectName   12_imagedownload

  5.     * @brief         mainwindow.h

  6.     * [url=home.php?mod=space&uid=90321]@Author[/url]        Deng Zhimao

  7.     * [url=home.php?mod=space&uid=55957]@EMAIL[/url]         1252699831@qq.com

  8.     * [url=home.php?mod=space&uid=28414]@net[/url]            www.openedv.com

  9.     * @date           2021-04-16

  10.     *******************************************************************/

  11. 1   #ifndef MAINWINDOW_H

  12. 2   #define MAINWINDOW_H

  13. 3

  14. 4   #include <QMainWindow>

  15. 5   #include <QNetworkAccessManager>

  16. 6   #include <QNetworkReply>

  17. 7   #include <QFile>

  18. 8   #include <QLabel>

  19. 9   #include <QPushButton>

  20. 10  #include <QProgressBar>

  21. 11  #include <QHBoxLayout>

  22. 12  #include <QVBoxLayout>

  23. 13  #include <QLineEdit>

  24. 14

  25. 15  class MainWindow : public QMainWindow

  26. 16  {

  27. 17      Q_OBJECT

  28. 18

  29. 19  public:

  30. 20      MainWindow(QWidget *parent = nullptr);

  31. 21      ~MainWindow();

  32. 22  private:

  33. 23      /* 网络管理 */

  34. 24      QNetworkAccessManager *networkAccessManager;

  35. 25

  36. 26      /* 标签 */

  37. 27      QLabel *label[3];

  38. 28

  39. 29      /* 按钮 */

  40. 30      QPushButton *pushButton;

  41. 31

  42. 32      /* 下载进度条 */

  43. 33      QProgressBar *progressBar;

  44. 34

  45. 35      /* 水平布局 */

  46. 36      QHBoxLayout *hBoxLayout[2];

  47. 37

  48. 38      /* 垂直布局 */

  49. 39      QVBoxLayout *vBoxLayout;

  50. 40

  51. 41      /* 水平容器 */

  52. 42      QWidget *hWidget[2];

  53. 43

  54. 44      /* 垂直容器 */

  55. 45      QWidget *vWidget;

  56. 46

  57. 47      /* 链接输入框 */

  58. 48      QLineEdit *lineEdit;

  59. 49

  60. 50  private slots:

  61. 51      /* 读取数据 */

  62. 52      void readyReadData();

  63. 53

  64. 54      /* 响应完成处理 */

  65. 55      void replyFinished();

  66. 56

  67. 57      /* 下载进度管理 */

  68. 58      void imageDownloadProgress(qint64, qint64);

  69. 59

  70. 60      /* 点击开始下载 */

  71. 61      void startDownload();

  72. 62

  73. 63      /* 响应错误处理函数 */

  74. 64      void networkReplyError(QNetworkReply::NetworkError);

  75. 65  };

  76. 66  #endif // MAINWINDOW_H
复制代码
头文件里主要是声明界面用的元素,及一些槽函数。重点是声明networkAccessManager。
在源文件“mainwindow.cpp”具体代码如下。
  1. mainwindow.cpp编程后的代码

  2.     /******************************************************************

  3.     Copyright &#169; Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.

  4.     * @projectName   12_imagedownload

  5.     * @brief         mainwindow.cpp

  6.     * @author        Deng Zhimao

  7.     * @email         1252699831@qq.com

  8.     * @net            www.openedv.com

  9.     * @date           2021-04-16

  10.     *******************************************************************/

  11. 1   #include "mainwindow.h"

  12. 2   #include <QMessageBox>

  13. 3   #include <QCoreApplication>

  14. 4

  15. 5   MainWindow::MainWindow(QWidget *parent)

  16. 6       : QMainWindow(parent)

  17. 7   {

  18. 8       /* 设置主窗体的位置与大小 */

  19. 9       this->setGeometry(0, 0, 800, 480);

  20. 10

  21. 11      /* 标签0, 显示下载的图像 */

  22. 12      label[0] = new QLabel();

  23. 13      /* 标签1, 显示URL标签 */

  24. 14      label[1] = new QLabel();

  25. 15      /* 下载进度标签 */

  26. 16      label[2] = new QLabel();

  27. 17

  28. 18      /* 下载图片链接输入框 */

  29. 19      lineEdit = new QLineEdit();

  30. 20

  31. 21      /* 下载按钮 */

  32. 22      pushButton = new QPushButton();

  33. 23

  34. 24      /* 下载进度条 */

  35. 25      progressBar = new QProgressBar();

  36. 26

  37. 27      /* 水平布局 */

  38. 28      hBoxLayout[0] = new QHBoxLayout();

  39. 29      hBoxLayout[1] = new QHBoxLayout();

  40. 30

  41. 31      /* 垂直布局 */

  42. 32      vBoxLayout = new QVBoxLayout();

  43. 33

  44. 34      /* 水平容器 */

  45. 35      hWidget[0] = new QWidget();

  46. 36      hWidget[1] = new QWidget();

  47. 37

  48. 38      /* 垂直容器 */

  49. 39      vWidget = new QWidget();

  50. 40

  51. 41      label[1]->setText("URL链接:");

  52. 42      label[2]->setText("文件下载进度:");

  53. 43

  54. 44      pushButton->setText("下载");

  55. 45

  56. 46      /* 设置下载链接地址 */

  57. 47      lineEdit->setText("https://ss0.bdstatic.com/70cFuH"

  58. 48                        "Sh_Q1YnxGkpoWK1HF6hhy/it/u=42710"

  59. 49                        "87328,1384669424&fm=11&gp=0.jpg");

  60. 50      /* 设置标签的最小显示大小 */

  61. 51      label[0]->setMinimumSize(this->width(),

  62. 52                               this->height() * 0.75);

  63. 53

  64. 54      /* 根据文本文字大小自适应大小 */

  65. 55      label[1]->setSizePolicy(QSizePolicy::Fixed,

  66. 56                              QSizePolicy::Fixed);

  67. 57      label[2]->setSizePolicy(QSizePolicy::Fixed,

  68. 58                              QSizePolicy::Fixed);

  69. 59      pushButton->setSizePolicy(QSizePolicy::Fixed,

  70. 60                                QSizePolicy::Fixed);

  71. 61

  72. 62      /* 水平布局0添加元素 */

  73. 63      hBoxLayout[0]->addWidget(label[1]);

  74. 64      hBoxLayout[0]->addWidget(lineEdit);

  75. 65      hBoxLayout[0]->addWidget(pushButton);

  76. 66

  77. 67      /* 设置水平布局0为水平容器的布局0 */

  78. 68      hWidget[0]->setLayout(hBoxLayout[0]);

  79. 69

  80. 70      /* 水平布局1添加元素 */

  81. 71      hBoxLayout[1]->addWidget(label[2]);

  82. 72      hBoxLayout[1]->addWidget(progressBar);

  83. 73

  84. 74      /* 设置水平布局1为水平容器的布局1 */

  85. 75      hWidget[1]->setLayout(hBoxLayout[1]);

  86. 76

  87. 77      /* 垂直布局添加元素 */

  88. 78      vBoxLayout->addWidget(label[0]);

  89. 79      vBoxLayout->addWidget(hWidget[0]);

  90. 80      vBoxLayout->addWidget(hWidget[1]);

  91. 81

  92. 82      /* 设置垂直布局为垂直容器的布局 */

  93. 83      vWidget->setLayout(vBoxLayout);

  94. 84

  95. 85      /* 设置居中 */

  96. 86      setCentralWidget(vWidget);

  97. 87

  98. 88      /* 网络管理 */

  99. 89      networkAccessManager = new QNetworkAccessManager(this);

  100. 90

  101. 91      /* 信号槽连接 */

  102. 92      connect(pushButton, SIGNAL(clicked()),

  103. 93              this, SLOT(startDownload()));

  104. 94

  105. 95  }

  106. 96

  107. 97  MainWindow::~MainWindow()

  108. 98  {

  109. 99  }

  110. 100

  111. 101 void MainWindow::startDownload()

  112. 102 {

  113. 103     /* 获取URL链接 */

  114. 104     QUrl newUrl(QUrl(lineEdit->text()));

  115. 105

  116. 106     /* 如果下载链接无效,则直接返回 */

  117. 107     if (!newUrl.isValid()) {

  118. 108         QMessageBox::information(this, "error", "invalid url");

  119. 109         return;

  120. 110     }

  121. 111

  122. 112     /* 网络请求 */

  123. 113     QNetworkRequest  networkRequest;

  124. 114

  125. 115     /* 设置下载的地址 */

  126. 116     networkRequest.setUrl(newUrl);

  127. 117

  128. 118     /* 网络响应 */

  129. 119     QNetworkReply *newReply =

  130. 120             networkAccessManager->get(networkRequest);

  131. 121

  132. 122     /* 信号槽连接 */

  133. 123     connect(newReply, SIGNAL(finished()),

  134. 124             this, SLOT(replyFinished()));

  135. 125     connect(newReply, SIGNAL(readyRead()),

  136. 126             this, SLOT(readyReadData()));

  137. 127     connect(newReply, SIGNAL(downloadProgress(qint64, qint64)),

  138. 128             this, SLOT(imageDownloadProgress(qint64, qint64)));

  139. 129     connect(newReply,

  140. 130             SIGNAL(error(QNetworkReply::NetworkError)),

  141. 131             this,

  142. 132             SLOT(networkReplyError(QNetworkReply::NetworkError )));

  143. 133 }

  144. 134

  145. 135 void MainWindow::readyReadData()

  146. 136 {

  147. 137     /* 设置按钮不可用,防止未完成,再次点击 */

  148. 138     pushButton->setEnabled(false);

  149. 139

  150. 140     /* 获取信号发送者 */

  151. 141     QNetworkReply *reply = (QNetworkReply *)sender();

  152. 142

  153. 143     QFile imageFile;

  154. 144     /* 保存到当前路径,名称为"下载的.jpg" */

  155. 145     imageFile.setFileName(QCoreApplication::applicationDirPath()

  156. 146                           + "/下载的.jpg");

  157. 147

  158. 148     /* 如果此图片已经存在,则删除 */

  159. 149     if (imageFile.exists())

  160. 150         imageFile.remove();

  161. 151

  162. 152     /* 读取数据 */

  163. 153     QByteArray data =  reply->readAll();

  164. 154     /* 如果数据为空,返回 */

  165. 155     if (data.isEmpty()) {

  166. 156         qDebug()<<"data is null, please try it again!"<<endl;

  167. 157         return;

  168. 158     }

  169. 159

  170. 160     /* 判断是不是JPG格式的图片,如果不是则返回 */

  171. 161     if (! (data[0] == (char)0xff

  172. 162            && data[1] == (char)0xd8

  173. 163            && data[data.size() - 2] == (char)0xff

  174. 164            && data[data.size() - 1] == (char)0xd9)) {

  175. 165         qDebug()<<"not JPG data, please try it again!"<<endl;

  176. 166         return;

  177. 167     }

  178. 168

  179. 169     /* 转为QPixmap */

  180. 170     QPixmap pixmap;

  181. 171     pixmap.loadFromData(data);

  182. 172     pixmap.save(imageFile.fileName());

  183. 173 }

  184. 174

  185. 175 void MainWindow::replyFinished()

  186. 176 {

  187. 177     /* 获取信号发送者 */

  188. 178     QNetworkReply *reply = (QNetworkReply *)sender();

  189. 179

  190. 180     /* 防止内存泄漏 */

  191. 181     reply->deleteLater();

  192. 182

  193. 183     /* 判断当前执行程序下的图像是否下载完成 */

  194. 184     QFile imageFile(QCoreApplication::applicationDirPath()

  195. 185                     + "/下载的.jpg");

  196. 186     if (imageFile.exists()) {

  197. 187         /* 显示下载的图像 */

  198. 188         label[0]->setPixmap(QPixmap(imageFile.fileName()));

  199. 189         qDebug() <<"已经成功下载,文件路径为:"

  200. 190                 <<imageFile.fileName()<<endl;

  201. 191     } else

  202. 192         /* 清空显示 */

  203. 193         label[0]->clear();

  204. 194

  205. 195     /* 设置按钮可用 */

  206. 196     pushButton->setEnabled(true);

  207. 197 }

  208. 198

  209. 199 void MainWindow::imageDownloadProgress(qint64 bytes,

  210. 200                                        qint64 totalBytes)

  211. 201 {

  212. 202     /* 设置进度条的最大值 */

  213. 203     progressBar->setMaximum(totalBytes);

  214. 204     /* 设置当前值 */

  215. 205     progressBar->setValue(bytes);

  216. 206 }

  217. 207

  218. 208 /* 网络响应处理函数 */

  219. 209 void MainWindow::networkReplyError(QNetworkReply::NetworkError

  220. 210                                    error)

  221. 211 {

  222. 212     switch (error) {

  223. 213     case QNetworkReply::ConnectionRefusedError:

  224. 214         qDebug()<<"远程服务器拒绝连接"<<endl;

  225. 215         break;

  226. 216     case QNetworkReply::HostNotFoundError:

  227. 217         qDebug()<<"找不到远程主机名"<<endl;

  228. 218         break;

  229. 219     case QNetworkReply::TimeoutError:

  230. 220         qDebug()<<"与远程服务器连接超时"<<endl;

  231. 221         break;

  232. 222     default:

  233. 223         break;

  234. 224     }

  235. 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图片了。注意:此程序里的下载链接可能失效,请替换自己的图片链接。
image022.jpg

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-1-19 08:15

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表