说明:本文介绍多线程断点下载。项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件。因为实现过程较为复杂,所以下面贴出完整的代码。
实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100m,那么就在沙盒中创建一个100m的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分)。
项目中用到的主要类如下:
完成的实现代码如下:
主控制器中的代码:
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
|
#import "yyviewcontroller.h"
#import "yyfilemultidownloader.h"
@interface yyviewcontroller ()
@property (nonatomic, strong) yyfilemultidownloader *filemultidownloader;
@end
@implementation yyviewcontroller
- (yyfilemultidownloader *)filemultidownloader
{
if (!_filemultidownloader) {
_filemultidownloader = [[yyfilemultidownloader alloc] init];
// 需要下载的文件远程url
_filemultidownloader.url = @ "http://192.168.1.200:8080/mjserver/resources/jre.zip" ;
// 文件保存到什么地方
nsstring *caches = [nssearchpathfordirectoriesindomains(nscachesdirectory, nsuserdomainmask, yes) lastobject];
nsstring *filepath = [caches stringbyappendingpathcomponent:@ "jre.zip" ];
_filemultidownloader.destpath = filepath;
}
return _filemultidownloader;
}
- ( void )viewdidload
{
[super viewdidload];
}
- ( void )touchesbegan:(nsset *)touches withevent:(uievent *)event
{
[self.filemultidownloader start];
}
@end
|
自定义一个基类
yyfiledownloader.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
|
#import <foundation/foundation.h>
@interface yyfiledownloader : nsobject
{
bool _downloading;
}
/**
* 所需要下载文件的远程url(连接服务器的路径)
*/
@property (nonatomic, copy) nsstring *url;
/**
* 文件的存储路径(文件下载到什么地方)
*/
@property (nonatomic, copy) nsstring *destpath;
/**
* 是否正在下载(有没有在下载, 只有下载器内部才知道)
*/
@property (nonatomic, readonly, getter = isdownloading) bool downloading;
/**
* 用来监听下载进度
*/
@property (nonatomic, copy) void (^progresshandler)( double progress);
/**
* 开始(恢复)下载
*/
- ( void )start;
/**
* 暂停下载
*/
- ( void )pause;
@end
|
yyfiledownloader.m文件
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
|
#import "yyfiledownloader.h"
@implementation yyfiledownloader
@end
下载器类继承自yyfiledownloader这个类
yyfilesingdownloader.h文件
#import "yyfiledownloader.h"
@interface yyfilesingledownloader : yyfiledownloader
/**
* 开始的位置
*/
@property (nonatomic, assign) long long begin;
/**
* 结束的位置
*/
@property (nonatomic, assign) long long end;
@end
yyfilesingdownloader.m文件
#import "yyfilesingledownloader.h"
@interface yyfilesingledownloader() <nsurlconnectiondatadelegate>
/**
* 连接对象
*/
@property (nonatomic, strong) nsurlconnection *conn;
/**
* 写数据的文件句柄
*/
@property (nonatomic, strong) nsfilehandle *writehandle;
/**
* 当前已下载数据的长度
*/
@property (nonatomic, assign) long long currentlength;
@end
@implementation yyfilesingledownloader
- (nsfilehandle *)writehandle
{
if (!_writehandle) {
_writehandle = [nsfilehandle filehandleforwritingatpath:self.destpath];
}
return _writehandle;
}
/**
* 开始(恢复)下载
*/
- ( void )start
{
nsurl *url = [nsurl urlwithstring:self.url];
// 默认就是get请求
nsmutableurlrequest *request = [nsmutableurlrequest requestwithurl:url];
// 设置请求头信息
nsstring *value = [nsstring stringwithformat:@ "bytes=%lld-%lld" , self.begin + self.currentlength, self.end];
[request setvalue:value forhttpheaderfield:@ "range" ];
self.conn = [nsurlconnection connectionwithrequest:request delegate:self];
_downloading = yes;
}
/**
* 暂停下载
*/
- ( void )pause
{
[self.conn cancel];
self.conn = nil;
_downloading = no;
}
#pragma mark - nsurlconnectiondatadelegate 代理方法
/**
* 1. 当接受到服务器的响应(连通了服务器)就会调用
*/
- ( void )connection:(nsurlconnection *)connection didreceiveresponse:(nsurlresponse *)response
{
}
/**
* 2. 当接受到服务器的数据就会调用(可能会被调用多次, 每次调用只会传递部分数据)
*/
- ( void )connection:(nsurlconnection *)connection didreceivedata:(nsdata *)data
{
// 移动到文件的尾部
[self.writehandle seektofileoffset:self.begin + self.currentlength];
// 从当前移动的位置(文件尾部)开始写入数据
[self.writehandle writedata:data];
// 累加长度
self.currentlength += data.length;
// 打印下载进度
double progress = ( double )self.currentlength / (self.end - self.begin);
if (self.progresshandler) {
self.progresshandler(progress);
}
}
/**
* 3. 当服务器的数据接受完毕后就会调用
*/
- ( void )connectiondidfinishloading:(nsurlconnection *)connection
{
// 清空属性值
self.currentlength = 0;
// 关闭连接(不再输入数据到文件中)
[self.writehandle closefile];
self.writehandle = nil;
}
/**
* 请求错误(失败)的时候调用(请求超时\断网\没有网, 一般指客户端错误)
*/
- ( void )connection:(nsurlconnection *)connection didfailwitherror:(nserror *)error
{
}
@end
|
设计多线程下载器(利用hmfilemultidownloader能开启多个线程同时下载一个文件)
一个多线程下载器只下载一个文件
yyfilemultidownloader.h文件
1
2
3
|
#import "yyfiledownloader.h"
@interface yyfilemultidownloader : yyfiledownloader
@end
|
yyfilemultidownloader.m文件
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
|
#import "yyfilemultidownloader.h"
#import "yyfilesingledownloader.h"
#define yymaxdownloadcount 4
@interface yyfilemultidownloader()
@property (nonatomic, strong) nsmutablearray *singledownloaders;
@property (nonatomic, assign) long long totallength;
@end
@implementation yyfilemultidownloader
- ( void )getfilesize
{
nsmutableurlrequest *request = [nsmutableurlrequest requestwithurl:[nsurl urlwithstring:self.url]];
request.httpmethod = @ "head" ;
nsurlresponse *response = nil;
#warning 这里要用异步请求
[nsurlconnection sendsynchronousrequest:request returningresponse:&response error:nil];
self.totallength = response.expectedcontentlength;
}
- (nsmutablearray *)singledownloaders
{
if (!_singledownloaders) {
_singledownloaders = [nsmutablearray array];
// 获得文件大小
[self getfilesize];
// 每条路径的下载量
long long size = 0;
if (self.totallength % yymaxdownloadcount == 0) {
size = self.totallength / yymaxdownloadcount;
} else {
size = self.totallength / yymaxdownloadcount + 1;
}
// 创建n个下载器
for ( int i = 0; i<yymaxdownloadcount; i++) {
yyfilesingledownloader *singledownloader = [[yyfilesingledownloader alloc] init];
singledownloader.url = self.url;
singledownloader.destpath = self.destpath;
singledownloader.begin = i * size;
singledownloader.end = singledownloader.begin + size - 1;
singledownloader.progresshandler = ^( double progress){
nslog(@ "%d --- %f" , i, progress);
};
[_singledownloaders addobject:singledownloader];
}
// 创建一个跟服务器文件等大小的临时文件
[[nsfilemanager defaultmanager] createfileatpath:self.destpath contents:nil attributes:nil];
// 让self.destpath文件的长度是self.totallengt
nsfilehandle *handle = [nsfilehandle filehandleforwritingatpath:self.destpath];
[handle truncatefileatoffset:self.totallength];
}
return _singledownloaders;
}
/**
* 开始(恢复)下载
*/
- ( void )start
{
[self.singledownloaders makeobjectsperformselector:@selector(start)];
_downloading = yes;
}
/**
* 暂停下载
*/
- ( void )pause
{
[self.singledownloaders makeobjectsperformselector:@selector(pause)];
_downloading = no;
}
@end
|
补充说明:如何获得将要下载的文件的大小?
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。