好好学习天天 2019-06-28
iOS开发过程中有时会有下载大文件的需求,如果不对文件做断点下载处理,一旦在下载过程中中断,再次请求会重新开始下载,对用户来说会消耗过多的流量(非WIFI情况下),非常不友好。
设置请求头Range
属性(需要文件服务器支持)。
https://github.com/TMWu/TMDow...
/** * 开启任务下载资源 * * @param url 下载地址 * @param progressBlock 回调下载进度 * @param stateBlock 下载状态 */ - (void)downloadWithUrl:(NSString *)url progress:(void(^)(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress))progressBlock state:(void(^)(DownloadState state))stateBlock;
/** * 查询该资源的下载进度值 * * @param url 下载地址 * * @return 返回下载进度值 */ - (CGFloat)progressWithUrl:(NSString *)url;
/** * 删除该资源 * * @param url 下载地址 */ - (void)deleteFileWithUrl:(NSString *)url;
/** * 停止下载任务 */ - (void)stopWithUrl:(NSString *)url;
/** * 获取文件路径 */ - (NSString *)getFilePathWithUrl:(NSString *)url;
TMDownloadManager
使用单例管理。
通过url
作为key,生成随机taskIdentifier
进行缓存。
执行downloadWithUrl:progress:state:
时会进行判断:
//判断url是否为空 if (!url) return; //判断是否已经下载完成 if ([self isCompletionWithUrl:url]) { stateBlock(DownloadStateCompleted); NSLog(@"----该资源已下载完成"); return; } //判断是否存在该任务 if ([self.tasks valueForKey:TMFileName(url)]) { [self handle:url]; return; } // 创建缓存目录文件 NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:TMCachesDirectory]) { [fileManager createDirectoryAtPath:TMCachesDirectory withIntermediateDirectories:YES attributes:nil error:NULL]; }
创建请求
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; // 创建流 NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:TMFileFullpath(url) append:YES]; // 创建请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; // 设置请求头 NSString *range = [NSString stringWithFormat:@"bytes=%f-", TMDownloadLength(url)]; [request setValue:range forHTTPHeaderField:@"Range"]; // 创建一个Data任务 NSURLSessionDataTask *task = [session dataTaskWithRequest:request]; //随机生成任务ID NSUInteger taskIdentifier = arc4random() % ((arc4random() % 10000 + arc4random() % 10000)); [task setValue:@(taskIdentifier) forKeyPath:@"taskIdentifier"]; // 保存任务 [self.tasks setValue:task forKey:TMFileName(url)]; TMSessionModel *sessionModel = [[TMSessionModel alloc] init]; sessionModel.url = url; sessionModel.progressBlock = progressBlock; sessionModel.stateBlock = stateBlock; sessionModel.stream = stream; [self.sessionModels setValue:sessionModel forKey:@(task.taskIdentifier).stringValue]; [self startWithUrl:url];
接收到数据时,写入数据
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { TMSessionModel *sessionModel = [self getSessionModel:dataTask.taskIdentifier]; // 写入数据 [sessionModel.stream write:data.bytes maxLength:data.length]; // 下载进度 NSUInteger receivedSize = TMDownloadLength(sessionModel.url); NSUInteger expectedSize = sessionModel.totalLength; CGFloat progress = 1.0 * receivedSize / expectedSize; sessionModel.progressBlock(receivedSize, expectedSize, progress); }
下载完成后关闭流,并从任务列表中删除。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { TMSessionModel *sessionModel = [self getSessionModel:task.taskIdentifier]; if (!sessionModel) return; if ([self isCompletionWithUrl:sessionModel.url]) { // 下载完成 sessionModel.stateBlock(DownloadStateCompleted); } else if (error){ NSLog(@"%@", error); // 下载失败 sessionModel.stateBlock(DownloadStateFailed); } // 关闭流 [sessionModel.stream close]; sessionModel.stream = nil; // 清除任务 [self.tasks removeObjectForKey:TMFileName(sessionModel.url)]; [self.sessionModels removeObjectForKey:@(task.taskIdentifier).stringValue]; }