iOS開發過程當中有時會有下載大文件的需求,若是不對文件作斷點下載處理,一旦在下載過程當中中斷,再次請求會從新開始下載,對用戶來講會消耗過多的流量(非WIFI狀況下),很是不友好。git
設置請求頭Range
屬性(須要文件服務器支持)。github
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]; }
建立請求session
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];
接收到數據時,寫入數據app
- (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); }
下載完成後關閉流,並從任務列表中刪除。dom
- (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]; }