在QT中采用多线程下载文件

这里的线程是指下载的通道(和操作系统中的线程不一样),一个线程就是一个文件的下载通道,多线程也就是同时开起好几个下载通道.当服务器提供下载服务 时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。不难理解,如果你线程多的话,那下载的越快。现流行的下载软件 都支持多线程。   


思路:   
1:用阻塞的方式获取目标地址的HTTP头部,得到目标文件的大小。   
2:算出每段文件的开始点,结尾点,并分别向目标地址发出请求。   
3:每次目标地址有数据返回,都将得到的数据写入文件。   

4:等待各段文件下载结果。

 

运行截图:

在QT中采用多线程下载文件

 转载自:http://www.open-open.com/lib/view/open1328015303609.html

源代码:

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
#include <QtCore>
#include <QtNetwork>
 
//多线程下载的线程数
const int PointCount = 5;
//目标文件的地址(千千静听的下载地址,我用来做实验的)
const QString strUrl = "http://ttplayer.qianqian.com/otherdown/alladin/ttpsetup_5713.exe";
 
//用于下载文件(或文件的一部分)
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;
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值