最近在做一個小項目的時候,發現使用NSURLSession或者AFNNetworking進行斷點續傳時諸多的不便,於是自己封裝了一個類來實現斷點續傳,在程序重新啟動時仍然可以繼續下載(需自己調用方法),同時可以在同一時間多次調用該類方法。使用時請注意傳入各參數的合理性,方法內部並沒有對傳入的參數進行修正
主要技術: NSURLConnection、block、NFFileHandle
1、首先,我提供一個類方法,供外界調用。 創建的類名為DownloadService
復制代碼
1 //
2 // DownloadService.h
3 // 11111
4 //
5 // Created by Liu Feng on 14-2-17.
6 // Copyright (c) 2014年 Liu Feng. All rights reserved.
7 //
8
9 #import <Foundation/Foundation.h>
10
11 typedef void (^DownloadServiceSuccess)(NSString *savePath);
12 typedef void (^DownloadServiceFailure)(NSError *error);
13
14 @interface DownloadService : NSObject
15 /**
16 * 下載指定URL的資源到路徑
17 *
18 * @param urlStr 網絡資源路徑
19 * @param toPath 本地存儲文件夾
20 * @param capacity 緩存大小,單位為Mb
21 * @param success 成功時回傳本地存儲路徑
22 * @param failure 失敗時回調的錯誤原因
23 */
24 + (void)downLoadWithURL:(NSString *)urlStr toDirectory:(NSString *)toDirectory cacheCapacity:(NSUInteger)capacity success:(DownloadServiceSuccess)success failure:(DownloadServiceFailure)failure;
25
26 @end
復制代碼
2、在.m中實現
復制代碼
1 //
2 // DownloadService.m
3 // 11111
4 //
5 // Created by Liu Feng on 14-2-17.
6 // Copyright (c) 2014年 Liu Feng. All rights reserved.
7 //
8
9 #import "DownloadService.h"
10
11 static DownloadService *_download;
12 static NSMutableDictionary *_dictPath;
13 static NSMutableDictionary *_dictBlock;
14 static NSMutableDictionary *_dictHandle;
15 static unsigned long long _cacheCapacity; // 緩存
16 static NSMutableData *_cacheData;
17
18 typedef void (^myBlcok)(NSString *savePath, NSError *error);
19
20 @interface DownloadService ()<NSURLConnectionDataDelegate>
21
22 @end
23
24 @implementation DownloadService
25
26 + (void)initialize
27 {
28 _download = [[DownloadService alloc] init];
29 _dictPath = [NSMutableDictionary dictionary]; // 存儲文件路徑
30 _dictBlock = [NSMutableDictionary dictionary]; // 存儲block
31 _dictHandle = [NSMutableDictionary dictionary]; // 存儲NSFileHandle對象
32 _cacheData = [NSMutableData data]; // 存放緩存
33 }
34
35 + (void)downLoadWithURL:(NSString *)urlStr toDirectory:(NSString *)toDirectory cacheCapacity:(NSInteger)capacity success:(DownloadServiceSuccess)success failure:(DownloadServiceFailure)failure{
36
37 // 1. 創建文件
38 NSString *fileName = [urlStr lastPathComponent];
39 NSString *filePath = [NSString stringWithFormat:@"%@/%@", toDirectory, fileName];
40
41 // 記錄文件起始位置
42 unsigned long long from = 0;
43 if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]){ // 已經存在
44 from = [[NSData dataWithContentsOfFile:filePath] length];
45 }else{ // 不存在,直接創建
46 [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
47 }
48
49 // url
50 NSURL *url = [NSURL URLWithString:urlStr];
51
52 // 請求
53 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0f];
54
55 // 設置請求頭文件
56 NSString *rangeValue = [NSString stringWithFormat:@"bytes=%llu-", from];
57 [request addValue:rangeValue forHTTPHeaderField:@"Range"];
58
59 // 創建連接
60 NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:_download];
61
62 // 保存文章連接
63 _dictPath[connection.description] = filePath;
64
65 // 保存block,用於回調
66 myBlcok block = ^(NSString *savePath, NSError *error){
67 if (error) {
68 if (failure) {
69 failure(error);
70 }
71 }else{
72 if (success) {
73 success(savePath);
74 }
75 }
76 };
77 _dictBlock[connection.description] = block;
78
79 // 保存緩存大小
80 _cacheCapacity = capacity * 1024 * 1024;
81
82 // 開始連接
83 [connection start];
84 }
85 /**
86 * 接收到服務器響應
87 *
88 * @param connection 哪一個連接
89 * @param response 響應對象
90 */
91 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
92 {
93 // 取出文章地址
94 NSString *filePath = _dictPath[connection.description];
95
96 // 打開文件准備輸入
97 NSFileHandle *outFile = [NSFileHandle fileHandleForWritingAtPath:filePath];
98
99 // 保存文件操作對象
100 _dictHandle[connection.description] = outFile;
101 }
102 /**
103 * 開始接收數據
104 *
105 * @param connection 哪一個連接
106 * @param data 二進制數據
107 */
108 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
109 {
110 // 取出文件操作對象
111 NSFileHandle *outFile = _dictHandle[connection.description];
112
113 // 移動到文件結尾
114 [outFile seekToEndOfFile];
115
116 // 保存數據
117 [_cacheData appendData:data];
118
119 if (_cacheData.length >= _cacheCapacity) {
120 // 寫入文件
121 [outFile writeData:data];
122
123 // 清空數據
124 [_cacheData setLength:0];
125 }
126 }
127 /**
128 * 連接出錯
129 *
130 * @param connection 哪一個連接出錯
131 * @param error 錯誤信息
132 */
133 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
134 {
135 // 取出文件操作對象
136 NSFileHandle *outFile = _dictHandle[connection.description];
137
138 // 關閉文件操作
139 [outFile closeFile];
140
141 // 回調block
142 myBlcok block = _dictBlock[connection.description];
143
144 if (block) {
145 block(nil, error);
146 }
147
148 // 移除字典中
149 [_dictHandle removeObjectForKey:connection.description];
150 [_dictPath removeObjectForKey:connection.debugDescription];
151 [_dictBlock removeObjectForKey:connection.description];
152 }
153 /**
154 * 結束加載
155 *
156 * @param connection 哪一個連接
157 */
158 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
159 {
160 // 取出文件操作對象
161 NSFileHandle *outFile = _dictHandle[connection.description];
162
163 // 關閉文件操作
164 [outFile closeFile];
165
166 // 取出路徑
167 NSString *savePath = [_dictPath objectForKey:connection.description];
168
169 // 取出block
170 myBlcok block = _dictBlock[connection.description];
171
172 // 回調
173 if (block) {
174 block(savePath, nil);
175 }
176
177 // 移除字典中
178 [_dictHandle removeObjectForKey:connection.description];
179 [_dictPath removeObjectForKey:connection.debugDescription];
180 [_dictBlock removeObjectForKey:connection.description];
181 }
182
183
184 @end