在QT中采用多線程下載文件

fmms 12年前發布 | 97K 次閱讀 QT GUI開發框架

這里的線程是指下載的通道(和操作系統中的線程不一樣),一個線程就是一個文件的下載通道,多線程也就是同時開起好幾個下載通道.當服務器提供下載服務 時,使用下載者是共享帶寬的,在優先級相同的情況下,總服務器會對總下載線程進行平均分配。不難理解,如果你線程多的話,那下載的越快。現流行的下載軟件 都支持多線程。


思路:
1:用阻塞的方式獲取目標地址的HTTP頭部,得到目標文件的大小。
2:算出每段文件的開始點,結尾點,并分別向目標地址發出請求。
3:每次目標地址有數據返回,都將得到的數據寫入文件。

4:等待各段文件下載結果。

 

運行截圖:

在QT中采用多線程下載文件

 

源代碼:

#include <QtCore>

include <QtNetwork>

//多線程下載的線程數 const int PointCount = 5; //目標文件的地址(千千靜聽的下載地址,我用來做實驗的) const QString strUrl = "http://ttplayer.qianqian.com/otherdown/alladin/ttpsetup_5713.exe&quot;;

//用于下載文件(或文件的一部分) 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();

};

//用于管理文件的下載 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(); };

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.toAscii());

//開始下載
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();

}

//用阻塞的方式獲取下載文件的長度 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); delete reply; 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"; } }

include "main.moc"

int main(int argc, char *argv) { QCoreApplication app(argc, argv); //用阻塞的方式下載文件,完成后退出 DownloadControl control = new DownloadControl; QEventLoop loop; QObject::connect(control, SIGNAL(FileDownloadFinished()), &loop, SLOT(quit())); control->StartFileDownload(strUrl, PointCount); loop.exec(); return 0; }</pre>

 本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!