简述
最近在研究了一下用Qt 的方法来实现http下载,Qt 中的Http请求主要用到了QNetworkAccessManager、QNetworkReply、QNetworkRequest 这三块。本篇文章主要叙述如何用Qt 的方法进行HTTP 请求下载文件,能够支持断点续传(断点续传即能够手动停止下载,下次可以从已经下载的部分开始继续下载未完成的部分,而没有必要从头开始上传下载),并且实时更新下载信息。整体代码考虑十分周到,对各种情况也做了相应的处理,并且有通俗易懂的注释。好了,代码走起!
代码之路
在讲解代码之前先看一下效果图:
效果:
从图中可以看出点击start按钮,进行下载,stop按钮暂停当前下载,close按钮停止当前下载,并删除已经下载的临时文件,并将所有参数重置, 这里界面中下载链接输入框为空是因为我在代码中默认了url,也可以在输入框中输入url进行下载。
代码主要包含两个部分:
1、DownLoadManager : 用来请求下载,向界面传递下载信息,并将下载的内容保存到文件中
2、MyHttpDownload : 用来接收下载链接,利用DownLoadManager进行下载,更新界面,并对当前下载进行操作(包括:开始、暂停、停止下载)。
1、DownLoadManager
#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){ if (m_isStop) { m_isStop = false; m_url = QUrl(url); m_fileName = fileName + DOWNLOAD_FILE_SUFFIX; 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; 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; 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(){ 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);}
- 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
2、MyHttpDownload
#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.3sMyHttpDownload::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))); } 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){ qDebug() << QString("%1").arg(bytesReceived * 100 / bytesTotal + 1); ui.progressBar->setMaximum(bytesTotal); ui.progressBar->setValue(bytesReceived); m_intervalDownload += bytesReceived - m_currentDownload; m_currentDownload = bytesReceived; uint timeNow = m_timeRecord.elapsed(); 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;}
- 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
标注: 代码注释中提到可以根据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()方法也可能返回为空,所以不是都有效。
好了,代码到此也就结束了,整体代码不算很难理解,也算是比较简单的一个http请求下载的小实例,后续我将会继续对http下载进行分析,具体包括将当前下载信息保存到本地、对http请求的文件进行分块下载等,欢迎大家一起交流。
代码下载
尾
看代码其实实现起来不是那么难,但是一步想把功能做全做完善不是那么容易的事,我希望的是能够尽善尽美。在编码过程中也遇到了很多的问题包括下载速度的计算,下载信息的更新,与界面的交互这些都需要处理好。涉及到计算就要把握计算的值是否正确(或者说准确)以及是否计算结果越界导致程序崩溃。就如何控制下载速度的计算,我想不同的人有不同的思路,算出来的结果也不一定完全一致,但是大致时间段的速度应该是相近的,也可以用360等工具进行大致的测试。整篇文章的代码是经过不断地测试,改进之后的,代码中可能存在问题或者有一些不适当,如有还请高人指出。
这两个月来写博客的经历,让我觉得认认真真写一篇博客就像写一篇精美的文章一样,需要经过深思熟虑,前后推敲,反复修改。在写此篇博客之前,我对代码进行了整理,同时也加上了一些注释。其实有时候过多的注释也显得多余,好的代码需要足够精简但在一定程度上又需要一定的可阅读性。而可阅读性的代码胜过大量的注释,所以在编码实现功能的同时需要对自己的代码进行自我审视。子曰: 吾日三省吾身,不亦说乎。因此,在我们飞快地敲击键盘来码代码的同时要放慢速度,留下足够的时间来检测自己的代码。希望在以后的学习过程中,能够养成良好的编程习惯,不快不慢,不急不躁,Keep Moving。
不知不觉,夜已深,前行中的小猪仍在前行中——窗外弥留着虫儿的鸣叫声,在蒙蒙的秋雨中显得更加清脆,悦耳。
FROM:
http://blog.csdn.net/GoForwardToStep/article/details/52704464