本篇文章介绍文件下载,包括以下内容:
- 原生态的分段下载的基本原理
- NSURLSession的下载任务
- 使用AFNetworking下载
- 下载任务常遇状况
- 原生态的分段下载的基本原理
文件下载往往需要比较长的一段时间,实现分段下载是实现断点下载的前提
实现分段下载需要解决以下两个问题
问题1:下载前,需要知道文件的长度
问题2:每一次请求下载文件的一段
- 针对问题1:HEAD请求获取文件长度
HEAD请求只获取响报文,而不获取资源的实际数据
通常用来获取文件长度、检测服务器端资源是否发生变化
响应对象NSURLResponse的属性exceptedContentLength即表示文件的长度
- 针对问题2:请求头参数Range进行分段下载
HTTP协议规定,请求头中的Range参数用于请求一段数据
如:
bytes=0-499 从0到499的头500个字节
bytes=500-999 从500到999的第二个500字节
bytes=500- 从500字节以后的所有字节
bytes=-500 最后500个字节
bytes=500-599,800-899 同时指定几个范围
也就是说,分段下载,就是发送多次多次请求,每次请求设置Range字段
下面的代码,完成一个文件的分段下载:
下面的代码,完成文件的一段下载:
使用异步下载时,应使用代理、通知、block进行下载后的处理动作。
- NSURLSession的下载任务
下载任务的创建:
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL*)url - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL*)url completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL*location, NSURLResponse *response, NSError*error))completionHandler
NSURLSession执行下载任务相关的代理方法:
// 周期性的调用,参数描述了下载的进度 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite // 下载完成时调用 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL *)location
示例说明:
1)创建NSURLSession对象并指定代理
2)创建下载任务并启动
3)实现下载完成的代理方法:应将下载完成后的文件移动到指定目录并重命名
4)实现下载进度监听的代理方法:
- 下载暂停/继续的实现
NSURLSessionDownloadTask的暂停方法:
- (void)cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler
参数resumeData:描述断点信息
NSURLSession通过断点信息创建下载任务:
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeDatacompletionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError*error))completionHandler
相关代理方法:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
实例说明:
1)下载暂停
2)下载继续
3)断点继续下载的代理方法
- 使用AFNetworking下载
使用AFNetworking进行下载,本质上还是创建NSURLSessionDownloadTask对象
只是将代理方法的封装到AFURLSession对象中,并用block指定行为
AFURLSessionManager创建下载任务
// 根据请求对象创建下载任务 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(NSProgress *__nullable __autoreleasing *__nullable)progress destination:(nullable NSURL *( ^ ) ( NSURL *targetPath , NSURLResponse *response ))destination completionHandler:(nullable void ( ^ ) ( NSURLResponse *response , NSURL *filePath , NSError *error ))completionHandler
// 根据断点信息创建爱下载任务 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData progress:(NSProgress *__nullable __autoreleasing *__nullable)progress destination:(nullable NSURL *( ^ ) ( NSURL *targetPath , NSURLResponse *response ))destination completionHandler:(nullable void ( ^ ) ( NSURLResponse *response , NSURL *filePath , NSError *error ))completionHandler
参数destination:该block用于指定获取文件下载后的路径
参数completionHandler:该block在下载任务完成时执行
包括任务被暂停时(cancelByProducingResumeData)
出参progress:非NULL时,会返回一个NSProgress对象,描述下载进度
示例说明:
1)创建NSURLSession对象并指定代理
2)下载完成的block封装1:应返回下载完成的文件的最终存储URL
3)任务完成block封装:包括错误情况下的处理、成功请求下的提示等
4)下载任务的创建启动/继续
5)下载任务的暂停
- 使用NSProgress监听下载进度
在上面的代码中,有一个参数NSProgress类型,传的是NULL,这个参数是AFN框架用于监听下载进度
NSProgress在iOS7.0引入,使用观察者模式
其fractionCompleted属性值变化时,通知其所有的观察者
示例说明:
1)下载任务的创建启动/继续
2)KVO的响应方法
其中self.progressView是一个自定义的用于显示下载进度的视图对象
- 下载任务常遇状况
状况一:程序退出时,如何保存下载状态
下载任务管理类,注册成为以下UIApplication对象通知的响应者
UIApplicationDidEnterBackgroundNotification
UIApplicationWillTerminateNotification
响应方法实现:将所有的下载任务暂停,并将断点信息保存到本地的操作
响应下面通知,从本地读出断点信息的操作
UIApplicationWillEnterForegroundNotification
或在下载任务管理对象被创建时,从本地读出断点信息的操作
状况二:从断点继续下载时,如果服务器上的文件发生变化或被删除,应如何处理?
可以在从断点继续下载前,先发送一次HEAD请求,判断文件是否存在,以及该文件的长度是否发生变化等操作。