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

 

posted @ 2014-02-19 09:42  2020_xx  阅读(2056)  评论(1编辑  收藏  举报