iOS开发网络编程之断点续传-NSURLConnection
最近在做一个小项目的时候,发现使用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