这里的线程是指下载的通道(和操作系统中的线程不一样),一个线程就是一个文件的下载通道,多线程也就是同时开起好几个下载通道.当服务器提供下载服务时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。不难理解,如果你线程多的话,那下载的越快。现流行的下载软件都支持多线程。
思路:
1:用阻塞的方式获取目标地址的HTTP头部,得到目标文件的大小。
2:算出每段文件的开始点,结尾点,并分别向目标地址发出请求。
3:每次目标地址有数据返回,都将得到的数据写入文件。
源代码:
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <QCoreApplication> #include "downloadcontrol.h" //多线程下载的线程数 const int PointCount = 5; //目标文件的地址(千千静听的下载地址,我用来做实验的) int main(int argc, char **argv) { QCoreApplication a(argc, argv); //用阻塞的方式下载文件,完成后退出 DownloadControl *control = new DownloadControl; QEventLoop loop; QObject::connect(control, SIGNAL(FileDownloadFinished()), &loop, SLOT(quit())); control->StartFileDownload(strUrl, PointCount); loop.exec(); control->deleteLater(); return 0; } |
downloadcontrol.h
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 |
#ifndef DOWNLOADCONTROL_H #define DOWNLOADCONTROL_H #include <QObject> #include <QFile> #include <QUrl> const QString strUrl = "http://ttplayer.qianqian.com/otherdown/alladin/ttpsetup_5713.exe"; //用于管理文件的下载 class DownloadControl : public QObject { Q_OBJECT private: int m_DownloadCount; int m_FinishedNum; int m_FileSize; QUrl m_Url; QFile *m_File; public: DownloadControl(QObject *parent = 0); void StartFileDownload(const QString &url, int count); qint64 GetFileSize(QUrl url); signals: void FileDownloadFinished(); private slots: void SubPartFinished(); }; #endif // DOWNLOADCONTROL_H |
downloadcontrol.cpp
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 |
#include "downloadcontrol.h" #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QEventLoop> #include <QFileInfo> #include "download.h" //用阻塞的方式获取下载文件的长度 qint64 DownloadControl::GetFileSize(QUrl url) { QNetworkAccessManager manager; qDebug() << "Getting the file size..."; QEventLoop loop; //发出请求,获取目标地址的头部信息 QNetworkReply *reply = manager.head(QNetworkRequest(url)); QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()), Qt::DirectConnection); loop.exec(); QVariant var = reply->header(QNetworkRequest::ContentLengthHeader); reply->deleteLater(); qint64 size = var.toLongLong(); qDebug() << "The file size is: " << size; return size; } DownloadControl::DownloadControl(QObject *parent) : QObject(parent) { m_DownloadCount = 0; m_FinishedNum = 0; m_FileSize = 0; m_File = new QFile; } void DownloadControl::StartFileDownload(const QString &url, int count) { m_DownloadCount = count; m_FinishedNum = 0; m_Url = QUrl(url); m_FileSize = GetFileSize(m_Url); //先获得文件的名字 QFileInfo fileInfo(m_Url.path()); QString fileName = fileInfo.fileName(); if (fileName.isEmpty()) fileName = "index.html"; m_File->setFileName(fileName); //打开文件 m_File->open(QIODevice::WriteOnly); Download *tempDownload; //将文件分成PointCount段,用异步的方式下载 qDebug() << "Start download file from " << strUrl; for(int i=0; i<m_DownloadCount; i++) { //先算出每段的开头和结尾(HTTP协议所需要的信息) int start = m_FileSize * i / m_DownloadCount; int end = m_FileSize * (i+1) / m_DownloadCount; if( i != 0 ) start--; //分段下载该文件 tempDownload = new Download(i+1, this); connect(tempDownload, SIGNAL(DownloadFinished()), this, SLOT(SubPartFinished())); connect(tempDownload, SIGNAL(DownloadFinished()), tempDownload, SLOT(deleteLater())); tempDownload->StartDownload(m_Url, m_File, start, end); } } void DownloadControl::SubPartFinished() { m_FinishedNum++; //如果完成数等于文件段数,则说明文件下载完毕,关闭文件,发生信号 if( m_FinishedNum == m_DownloadCount ) { m_File->close(); emit FileDownloadFinished(); qDebug() << "Download finished"; } } |
download.h
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 |
#ifndef DOWNLOAD_H #define DOWNLOAD_H #include <QObject> #include <QNetworkAccessManager> #include <QNetworkReply> #include <QFile> //用于下载文件(或文件的一部分) class Download : public QObject { Q_OBJECT private: QNetworkAccessManager m_Qnam; QNetworkReply *m_Reply; QFile *m_File; const int m_Index; qint64 m_HaveDoneBytes; qint64 m_StartPoint; qint64 m_EndPoint; public: Download(int index, QObject *parent = 0); void StartDownload(const QUrl &url, QFile *file, qint64 startPoint=0, qint64 endPoint=-1); signals: void DownloadFinished(); public slots: void FinishedSlot(); void HttpReadyRead(); }; #endif // DOWNLOAD_H |
download.cpp
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 |
#include "download.h" #include <QNetworkRequest> Download::Download(int index, QObject *parent) : QObject(parent), m_Index(index) { m_HaveDoneBytes = 0; m_StartPoint = 0; m_EndPoint = 0; m_File = NULL; } void Download::StartDownload(const QUrl &url, QFile *file, qint64 startPoint/* =0 */, qint64 endPoint/* =-1 */) { if( NULL == file ) return; m_HaveDoneBytes = 0; m_StartPoint = startPoint; m_EndPoint = endPoint; m_File = file; //根据HTTP协议,写入RANGE头部,说明请求文件的范围 QNetworkRequest qheader; qheader.setUrl(url); QString range; range.sprintf("Bytes=%lld-%lld", m_StartPoint, m_EndPoint); qheader.setRawHeader("Range", range.toLatin1()); //开始下载 qDebug() << "Part " << m_Index << " start download"; m_Reply = m_Qnam.get(QNetworkRequest(qheader)); connect(m_Reply, SIGNAL(finished()), this, SLOT(FinishedSlot())); connect(m_Reply, SIGNAL(readyRead()), this, SLOT(HttpReadyRead())); } //下载结束 void Download::FinishedSlot() { m_File->flush(); m_Reply->deleteLater(); m_Reply = 0; m_File = 0; qDebug() << "Part " << m_Index << " download finished"; emit DownloadFinished(); } void Download::HttpReadyRead() { if ( !m_File ) return; //将读到的信息写入文件 QByteArray buffer = m_Reply->readAll(); m_File->seek( m_StartPoint + m_HaveDoneBytes ); m_File->write(buffer); m_HaveDoneBytes += buffer.size(); } |
发表评论
要发表评论,您必须先登录。