【Qt】Qt Http 下载(转载)

QT之HTTP请求下载

标签: Qt HTTP

简述

最近在研究了一下用Qt 的方法来实现http下载,Qt 中的Http请求主要用到了QNetworkAccessManagerQNetworkReplyQNetworkRequest 这三块。本篇文章主要叙述如何用Qt 的方法进行HTTP 请求下载文件,能够支持****断点续传****(断点续传即能够手动停止下载,下次可以从已经下载的部分开始继续下载未完成的部分,而没有必要从头开始上传下载),并且实时更新下载信息。整体代码考虑十分周到,对各种情况也做了相应的处理,并且有通俗易懂的注释。好了,代码走起!

代码之路

在讲解代码之前先看一下效果图:

效果:

从图中可以看出点击start按钮,进行下载,stop按钮暂停当前下载,close按钮停止当前下载,并删除已经下载的临时文件,并将所有参数重置, 这里界面中下载链接输入框为空是因为我在代码中默认了url,也可以在输入框中输入url进行下载。


代码主要包含两个部分:

1、DownLoadManager : 用来请求下载,向界面传递下载信息,并将下载的内容保存到文件中

2、MyHttpDownload : 用来接收下载链接,利用DownLoadManager进行下载,更新界面,并对当前下载进行操作(包括:开始、暂停、停止下载)。

1、DOWNLOADMANAGER

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include "downloadmanager.h"
#include <QFile>
#include <QDebug>
#include <QFileInfo>
#include <QDir>

#define DOWNLOAD_FILE_SUFFIX "_tmp"

DownLoadManager::DownLoadManager(QObject *parent)
: QObject(parent)
, m_networkManager(NULL)
, m_url(QUrl(""))
, m_fileName("")
, m_isSupportBreakPoint(false)
, m_bytesReceived(0)
, m_bytesTotal(0)
, m_bytesCurrentReceived(0)
, m_isStop(true)
{
m_networkManager = new QNetworkAccessManager(this);
}

DownLoadManager::~DownLoadManager()
{}

// 设置是否支持断点续传;
void DownLoadManager::setDownInto(bool isSupportBreakPoint)
{
m_isSupportBreakPoint = isSupportBreakPoint;
}

// 获取当前下载链接;
QString DownLoadManager::getDownloadUrl()
{
return m_url.toString();
}

// 开始下载文件,传入下载链接和文件的路径;
void DownLoadManager::downloadFile(QString url , QString fileName)
{
// 防止多次点击开始下载按钮,进行多次下载,只有在停止标志变量为true时才进行下载;
if (m_isStop)
{
m_isStop = false;
m_url = QUrl(url);

// 这里可用从url中获取文件名,但不是对所有的url都有效;
// QString fileName = m_url.fileName();

// 将当前文件名设置为临时文件名,下载完成时修改回来;
m_fileName = fileName + DOWNLOAD_FILE_SUFFIX;

// 如果当前下载的字节数为0那么说明未下载过或者重新下载
// 则需要检测本地是否存在之前下载的临时文件,如果有则删除
if (m_bytesCurrentReceived <= 0)
{
removeFile(m_fileName);
}

QNetworkRequest request;
request.setUrl(m_url);

// 如果支持断点续传,则设置请求头信息
if (m_isSupportBreakPoint)
{
QString strRange = QString("bytes=%1-").arg(m_bytesCurrentReceived);
request.setRawHeader("Range", strRange.toLatin1());
}

// 请求下载;
m_reply = m_networkManager->get(request);

connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
connect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
}
}

// 下载进度信息;
void DownLoadManager::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
if (!m_isStop)
{
m_bytesReceived = bytesReceived;
m_bytesTotal = bytesTotal;
// 更新下载进度;(加上 m_bytesCurrentReceived 是为了断点续传时之前下载的字节)
emit signalDownloadProcess(m_bytesReceived + m_bytesCurrentReceived, m_bytesTotal + m_bytesCurrentReceived);
}
}

// 获取下载内容,保存到文件中;
void DownLoadManager::onReadyRead()
{
if (!m_isStop)
{
QFile file(m_fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Append))
{
file.write(m_reply->readAll());
}
file.close();
}
}

// 下载完成;
void DownLoadManager::onFinished()
{
m_isStop = true;
// http请求状态码;
QVariant statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);

if (m_reply->error() == QNetworkReply::NoError)
{
// 重命名临时文件;
QFileInfo fileInfo(m_fileName);
if (fileInfo.exists())
{
int index = m_fileName.lastIndexOf(DOWNLOAD_FILE_SUFFIX);
QString realName = m_fileName.left(index);
QFile::rename(m_fileName, realName);
}
}
else
{
// 有错误输出错误;
QString strError = m_reply->errorString();
qDebug() << "__________" + strError;
}

emit signalReplyFinished(statusCode.toInt());
}

// 下载过程中出现错误,关闭下载,并上报错误,这里未上报错误类型,可自己定义进行上报;
void DownLoadManager::onError(QNetworkReply::NetworkError code)
{
QString strError = m_reply->errorString();
qDebug() << "__________" + strError;

closeDownload();
emit signalDownloadError();
}

// 停止下载工作;
void DownLoadManager::stopWork()
{
m_isStop = true;
if (m_reply != NULL)
{
disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
disconnect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
disconnect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
disconnect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
m_reply->abort();
m_reply->deleteLater();
m_reply = NULL;
}
}

// 暂停下载按钮被按下,暂停当前下载;
void DownLoadManager::stopDownload()
{
// 这里m_isStop变量为了保护多次点击暂停下载按钮,导致m_bytesCurrentReceived 被不停累加;
if (!m_isStop)
{
//记录当前已经下载字节数
m_bytesCurrentReceived += m_bytesReceived;
stopWork();
}
}

// 重置参数;
void DownLoadManager::reset()
{
m_bytesCurrentReceived = 0;
m_bytesReceived = 0;
m_bytesTotal = 0;
}

// 删除文件;
void DownLoadManager::removeFile(QString fileName)
{
// 删除已下载的临时文件;
QFileInfo fileInfo(fileName);
if (fileInfo.exists())
{
QFile::remove(fileName);
}
}

// 停止下载按钮被按下,关闭下载,重置参数,并删除下载的临时文件;
void DownLoadManager::closeDownload()
{
stopWork();
reset();
removeFile(m_fileName);
}

2、MYHTTPDOWNLOAD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include "myhttpdownload.h"
#include "downloadmanager.h"
#include <QDebug>

#define UNIT_KB 1024 //KB
#define UNIT_MB 1024*1024 //MB
#define UNIT_GB 1024*1024*1024 //GB

#define TIME_INTERVAL 300 //0.3s

MyHttpDownload::MyHttpDownload(QWidget *parent)
: QWidget(parent)
, m_downloadManager(NULL)
, m_url("")
, m_timeInterval(0)
, m_currentDownload(0)
, m_intervalDownload(0)
{
ui.setupUi(this);
initWindow();
}

MyHttpDownload::~MyHttpDownload()
{

}

void MyHttpDownload::initWindow()
{
ui.progressBar->setValue(0);
connect(ui.pButtonStart, SIGNAL(clicked()), this, SLOT(onStartDownload()));
connect(ui.pButtonStop, SIGNAL(clicked()), this, SLOT(onStopDownload()));
connect(ui.pButtonClose, SIGNAL(clicked()), this, SLOT(onCloseDownload()));
// 进度条设置样式;
ui.progressBar->setStyleSheet("\
QProgressBar\
{\
border-width: 0 10 0 10;\
border-left: 1px, gray;\
border-right: 1px, gray;\
border-image:url(:/Resources/progressbar_back.png);\
}\
QProgressBar::chunk\
{\
border-width: 0 10 0 10;\
border-image:url(:/Resources/progressbar.png);\
}");
}

// 开始下载;
void MyHttpDownload::onStartDownload()
{
// 从界面获取下载链接;
m_url = ui.downloadUrl->text();
if (m_downloadManager == NULL)
{
m_downloadManager = new DownLoadManager(this);
connect(m_downloadManager , SIGNAL(signalDownloadProcess(qint64, qint64)), this, SLOT(onDownloadProcess(qint64, qint64)));
connect(m_downloadManager, SIGNAL(signalReplyFinished(int)), this, SLOT(onReplyFinished(int)));
}

// 这里先获取到m_downloadManager中的url与当前的m_url 对比,如果url变了需要重置参数,防止文件下载不全;
QString url = m_downloadManager->getDownloadUrl();
if (url != m_url)
{
m_downloadManager->reset();
}
m_downloadManager->setDownInto(true);
m_downloadManager->downloadFile(m_url, "F:/MyHttpDownload/MyDownloadFile.zip");
m_timeRecord.start();
m_timeInterval = 0;
ui.labelStatus->setText(QStringLiteral("正在下载"));
}

// 暂停下载;
void MyHttpDownload::onStopDownload()
{
ui.labelStatus->setText(QStringLiteral("停止下载"));
if (m_downloadManager != NULL)
{
m_downloadManager->stopDownload();
}
ui.labelSpeed->setText("0 KB/S");
ui.labelRemainTime->setText("0s");
}

// 关闭下载;
void MyHttpDownload::onCloseDownload()
{
m_downloadManager->closeDownload();
ui.progressBar->setValue(0);
ui.labelSpeed->setText("0 KB/S");
ui.labelRemainTime->setText("0s");
ui.labelStatus->setText(QStringLiteral("关闭下载"));
ui.labelCurrentDownload->setText("0 B");
ui.labelFileSize->setText("0 B");
}

// 更新下载进度;
void MyHttpDownload::onDownloadProcess(qint64 bytesReceived, qint64 bytesTotal)
{
// 输出当前下载进度;
// 用到除法需要注意除0错误;
qDebug() << QString("%1").arg(bytesReceived * 100 / bytesTotal + 1);
// 更新进度条;
ui.progressBar->setMaximum(bytesTotal);
ui.progressBar->setValue(bytesReceived);

// m_intervalDownload 为下次计算速度之前的下载字节数;
m_intervalDownload += bytesReceived - m_currentDownload;
m_currentDownload = bytesReceived;

uint timeNow = m_timeRecord.elapsed();

// 超过0.3s更新计算一次速度;
if (timeNow - m_timeInterval > TIME_INTERVAL)
{
qint64 ispeed = m_intervalDownload * 1000 / (timeNow - m_timeInterval);
QString strSpeed = transformUnit(ispeed, true);
ui.labelSpeed->setText(strSpeed);
// 剩余时间;
qint64 timeRemain = (bytesTotal - bytesReceived) / ispeed;
ui.labelRemainTime->setText(transformTime(timeRemain));

ui.labelCurrentDownload->setText(transformUnit(m_currentDownload));
ui.labelFileSize->setText(transformUnit(bytesTotal));

m_intervalDownload = 0;
m_timeInterval = timeNow;
}
}

// 下载完成;
void MyHttpDownload::onReplyFinished(int statusCode)
{
// 根据状态码判断当前下载是否出错;
if (statusCode >= 200 && statusCode < 400)
{
qDebug() << "Download Failed";
}
else
{
qDebug() << "Download Success";
}
}

// 转换单位;
QString MyHttpDownload::transformUnit(qint64 bytes , bool isSpeed)
{
QString strUnit = " B";
if (bytes <= 0)
{
bytes = 0;
}
else if (bytes < UNIT_KB)
{
}
else if (bytes < UNIT_MB)
{
bytes /= UNIT_KB;
strUnit = " KB";
}
else if (bytes < UNIT_GB)
{
bytes /= UNIT_MB;
strUnit = " MB";
}
else if (bytes > UNIT_GB)
{
bytes /= UNIT_GB;
strUnit = " GB";
}

if (isSpeed)
{
strUnit += "/S";
}
return QString("%1%2").arg(bytes).arg(strUnit);
}

// 转换时间;
QString MyHttpDownload::transformTime(qint64 seconds)
{
QString strValue;
QString strSpacing(" ");
if (seconds <= 0)
{
strValue = QString("%1s").arg(0);
}
else if (seconds < 60)
{
strValue = QString("%1s").arg(seconds);
}
else if (seconds < 60 * 60)
{
int nMinute = seconds / 60;
int nSecond = seconds - nMinute * 60;

strValue = QString("%1m").arg(nMinute);

if (nSecond > 0)
strValue += strSpacing + QString("%1s").arg(nSecond);
}
else if (seconds < 60 * 60 * 24)
{
int nHour = seconds / (60 * 60);
int nMinute = (seconds - nHour * 60 * 60) / 60;
int nSecond = seconds - nHour * 60 * 60 - nMinute * 60;

strValue = QString("%1h").arg(nHour);

if (nMinute > 0)
strValue += strSpacing + QString("%1m").arg(nMinute);

if (nSecond > 0)
strValue += strSpacing + QString("%1s").arg(nSecond);
}
else
{
int nDay = seconds / (60 * 60 * 24);
int nHour = (seconds - nDay * 60 * 60 * 24) / (60 * 60);
int nMinute = (seconds - nDay * 60 * 60 * 24 - nHour * 60 * 60) / 60;
int nSecond = seconds - nDay * 60 * 60 * 24 - nHour * 60 * 60 - nMinute * 60;

strValue = QString("%1d").arg(nDay);

if (nHour > 0)
strValue += strSpacing + QString("%1h").arg(nHour);

if (nMinute > 0)
strValue += strSpacing + QString("%1m").arg(nMinute);

if (nSecond > 0)
strValue += strSpacing + QString("%1s").arg(nSecond);
}

return strValue;
}

标注: 代码注释中提到可以根据URL来获取文件名,下方给予解释说明。

QString QUrl::fileName(ComponentFormattingOptions options = FullyDecoded) const
Returns the name of the file, excluding the directory path.
Note that, if this QUrl object is given a path ending in a slash, the name of the file is considered empty.
If the path doesn’t contain any slash, it is fully returned as the fileName.

在Qt助手中我们找到此方法,根据加粗的字段可以看出fileName()方法也可能返回为空,所以不是都有效。


【Qt】Qt Http 下载(转载)
https://hodlyounger.github.io/B_Code/Qt/【Qt】QtHttp下载/
作者
mingming
发布于
2023年10月27日
许可协议