說明:本文介紹多線程斷點下載。項目中使用了蘋果自帶的類,實現了同時開啟多條線程下載一個較大的文件。因為實現過程較為復雜,所以下面貼出完整的代碼。
實現思路:下載開始,創建一個和要下載的文件大小相同的文件(如果要下載的文件為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 |
補充說明:如何獲得將要下載的文件的大小?
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。