最近在做一個小項目的時候,發現使用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