最近用到了下载,网上也搜寻过下载方面的东西,没有找到太合适的关于AFNetWorking 3.x方面的断点续传的介绍或者demo,于是自己写吧。 AFURLSessionManager这个封装了上传、下载方面的相关内容,仔细阅读不难发现,这个就是对 NSURLSession 、NSURLSessionTask 进行的封装,下载、上传这些操作用到的就是NSURLSessionTask相关子类。 先说说第一种方式,就是最简单的下载,用到的方法显而易见
/**
第一种方式
*/
//方法包含了下载所需的 参数 回调 很全面, 这里返回一个NSURLSessionDownloadTask对象,用于调用系统的方法: resume开始、继续下载,suspend暂停下载,cancel取消下载
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
/**
第二种方式
可以实现一种不一样的断点续传 我个人感觉这种方式怪怪的 而且似乎不能做到关闭app后恢复断点续传,虽然可以保存之前下载的数据,但是下次的请求体就不存在了。
*/
//首先用第一种方法进行开始下载 然后后续暂停操作
//实现断点的关键地方 用这个方法进行取消操作 可以得到resumeData
- (void)cancelByProducingResumeData:(void (^)(NSData * _Nullable resumeData))completionHandler;
//然后调用的方法同系统方法
/*系统方法 解释 Creates a download task with the resume data. If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
/*AFN方法 继续进行下载*/
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
//上面这种方法 按照解释就是不能正常暂停,采用的一种特殊手段暂停下载,然后进行恢复下载,理论上说就是一种平常的下载,为了处理比较特殊的情况。至于更好的用法,我暂时还没有发现。
/**
第三种方式 本文要写的重点啦 这个方法其实就是代替了系统提供的代理方法进行下载各方面的操作 详情写在下面
*/
//获取文件大小
//文件大小
- (unsigned long long)fileSizeForPath:(NSString *)path {
unsigned long long fileSize = 0;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:path]) {
NSError * error = nil;
NSDictionary * fileDict = [fileManager attributesOfItemAtPath:path error:&error];
if (!error && fileDict) {
fileSize =[fileDict fileSize];
}
}
return fileSize;
}
//创建下载请求管理对象
AFURLSessionManager * manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
//创建流
NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:[NSURL fileURLWithPath:@"文件路径"] append:YES];
//请求体
NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:info.url]];
unsigned long long cacheFileSize = 0;
cacheFileSize = [self fileSizeForPath:@"文件路径"];
if (cacheFileSize) {
NSString *range = [NSString stringWithFormat:@"bytes=%lld-", cacheFileSize];
[request setValue:range forHTTPHeaderField:@"Range"];
}
//下载对象
NSURLSessionDataTask * task = [manager dataTaskWithRequest:request completionHandler:nil];
//下载接受数据
[manager setDataTaskDidReceiveResponseBlock:^NSURLSessionResponseDisposition(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSURLResponse * _Nonnull response) {
//下载文件的总大小
response.expectedContentLength + cacheFileSize
//打开流
[outputStream open];
return NSURLSessionResponseAllow;
}];
//写入数据
[manager setDataTaskDidReceiveDataBlock:^(NSURLSession * _Nonnull session, NSURLSessionDataTask * _Nonnull dataTask, NSData * _Nonnull data) {
NSInteger result = [outputStream write:data.bytes maxLength:data.length];
if (result == -1) {
//错误
outputStream.streamError;
[task cancel];
} else {
//正确操作
}
}];
//下载完成
[manager setTaskDidCompleteBlock:^(NSURLSession * _Nonnull session, NSURLSessionTask * _Nonnull task, NSError * _Nullable error) {
[outputStream close];
outputStream = nil;
task = nil;
if (error) {
}
}];
//⚠️ 上面的方法其实就是相当于系统的代理方法,具体的可以去查看 AFURLSessionManager.h 里面有详细的说明 demo待我整理整理 github奉上。