这里的线程是指下载的通道(和操作系统中的线程不一样),一个线程就是一个文件的下载通道,多线程也就是同时开起好几个下载通道.当服务器提供下载服务 时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。不难理解,如果你线程多的话,那下载的越快。现流行的下载软件 都支持多线程。
思路:
1:用阻塞的方式获取目标地址的HTTP头部,得到目标文件的大小。
2:算出每段文件的开始点,结尾点,并分别向目标地址发出请求。
3:每次目标地址有数据返回,都将得到的数据写入文件。
思路:
1:用阻塞的方式获取目标地址的HTTP头部,得到目标文件的大小。
2:算出每段文件的开始点,结尾点,并分别向目标地址发出请求。
3:每次目标地址有数据返回,都将得到的数据写入文件。
4:等待各段文件下载结果。
运行截图:
源代码:
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;
}
|