闲话
之前想着迅雷9那广告满天的界面,简直被恶心到了,就想着自己写个下载工具,结果花几个小时写好(copy好别人的代码),发现还是TM迅雷下载着舒服,毕竟人家那这么多年的P2P技术不是摆设,自己也只是写着玩玩儿,学习学习而已。程序运行图如下:
资源获取
要下载文件之前,当然要获取到资源文件,才可以开始下载,Qt实现了一个简单的资源获取方法:
/* 通过url获取文件名 */
QString Network::getFileInfo(QString task)
{
QUrl url = QUrl::fromEncoded(task.toLocal8Bit());
QString path = url.path();
QString fileName = QFileInfo(path).fileName();
return fileName;
}
在我们的面前就是传个url的path到QFileInfo类中,然后就可以返回资源名,很简单,当然只能获取简单的资源文件,要是像什么度娘上的图片或者度盘的链接,是识别不出来的。之前也看过别人的博客说可以先发http请求获取,不过自己没试过,也不知道是否可行。
开始下载
既然是一个HTTP资源下载工具,当然需要设计到HTTP请求,这里的请求很简单,其实也就是设置一下HTTP请求头而已:
QNetworkRequest request(url);
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
request.setRawHeader("Range", QString("bytes=%1-").arg(currentFileSize).toLatin1());
第一个请求头,产生请求的浏览器类型;
第二个请求头,接收文件的位置和范围。这个请求是最关键的,用来实现断点续传。
设置好请求头后,只需要连接几个接收的槽函数,就可以开始接收文件了:
reply = manager.get(request);
connect(reply, SIGNAL(readyRead()), this, SLOT(readyToRead()));
connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(downloadProgress(qint64,qint64)));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(downloadError(QNetworkReply::NetworkError)));
这几个槽的意思就很清楚了:
第一个,数据准备好,等着我们去读了;
第二个,文件接收完成;
第三个,接收的进度;
第四个,接收过程中发生的错误。
读函数,只需要往文件里写就对了:
void Network::readyToRead()
{
/* write data to file */
file.write(reply->readAll());
}
错误处理,这里我就只把错误打印出来,把下载停了就完事儿了,没做过多处理:
void Network::downloadError(QNetworkReply::NetworkError code)
{
qDebug() << "Failed: " << code;
stopTask(currentTask);
}
接收完成,向界面发送消息,同时把任务从下载列表中删除掉,如果任务队列不为空就开始下一个任务:
void Network::downloadFinished()
{
file.close();
timeElapsed = 0;
isDownloading = false;
downloadList.removeOne(currentTask);
if (reply->error()) {
qDebug() << "Failed: " << reply->errorString();
return ;
}
reply->deleteLater();
emit taskFinished(currentTask);
if (!downloadList.isEmpty()) {
QString taskNext = downloadList.first();
startDownload(taskNext, downloadPath);
}
}
从上面可以看出,这里也打印了错误信息,因为当发生错误,传输中断后,也会接收到finished()信号。
下载进度,在这可以获取下载到的文件大小,然后自己也可以实现速度计算:
void Network::downloadProgress(qint64 bytesReceived, qint64 byteTotal)
{
double speed = (bytesReceived * 1000.0) / (downloadTime.elapsed() + timeElapsed);
timeLast = downloadTime.elapsed();
// qDebug() << "Time elapse:" << downloadTime.elapsed() + timeElapsed;
double percent = (bytesReceived + currentFileSize) * 100.0 / (byteTotal + currentFileSize);
if (percent >= 100.0) {
downloadFinished();
return ;
}
emit process(currentTask, byteTotal + currentFileSize, percent, speed);
}
需要注意的是,如果是断点续传,这里获取到的byteTotal是还未接收的数据大小,所以需要把之前下载大小保存起来,当然也可以通过读已写文件大小来获取。
至于界面什么的,就不说了,不得不承认自己没有一点艺术细胞,代码下载地址:
https://github.com/DragonPang/QTDownloadTool