使用場景
iOS開發(fā)過程中有時會有下載大文件的需求,如果不對文件做斷點下載處理报慕,一旦在下載過程中中斷浅浮,再次請求會重新開始下載旭寿,對用戶來說會消耗過多的流量(非WIFI情況下),非常不友好溅话。
解決方案
設(shè)置請求頭Range
屬性(需要文件服務(wù)器支持)晓锻。
DEMO地址
https://github.com/TMWu/TMDownloadManager
主要方法
/**
* 開啟任務(wù)下載資源
*
* @param url 下載地址
* @param progressBlock 回調(diào)下載進度
* @param stateBlock 下載狀態(tài)
*/
- (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;
/**
* 停止下載任務(wù)
*/
- (void)stopWithUrl:(NSString *)url;
/**
* 獲取文件路徑
*/
- (NSString *)getFilePathWithUrl:(NSString *)url;
TMDownloadManager講解
TMDownloadManager
使用單例管理。
通過url
作為key飞几,生成隨機taskIdentifier
進行緩存砚哆。
執(zhí)行downloadWithUrl:progress:state:
時會進行判斷:
//判斷url是否為空
if (!url) return;
//判斷是否已經(jīng)下載完成
if ([self isCompletionWithUrl:url]) {
stateBlock(DownloadStateCompleted);
NSLog(@"----該資源已下載完成");
return;
}
//判斷是否存在該任務(wù)
if ([self.tasks valueForKey:TMFileName(url)]) {
[self handle:url];
return;
}
// 創(chuàng)建緩存目錄文件
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:TMCachesDirectory]) {
[fileManager createDirectoryAtPath:TMCachesDirectory withIntermediateDirectories:YES attributes:nil error:NULL];
}
創(chuàng)建請求
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
// 創(chuàng)建流
NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:TMFileFullpath(url) append:YES];
// 創(chuàng)建請求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
// 設(shè)置請求頭
NSString *range = [NSString stringWithFormat:@"bytes=%f-", TMDownloadLength(url)];
[request setValue:range forHTTPHeaderField:@"Range"];
// 創(chuàng)建一個Data任務(wù)
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
//隨機生成任務(wù)ID
NSUInteger taskIdentifier = arc4random() % ((arc4random() % 10000 + arc4random() % 10000));
[task setValue:@(taskIdentifier) forKeyPath:@"taskIdentifier"];
// 保存任務(wù)
[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];
接收到數(shù)據(jù)時,寫入數(shù)據(jù)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
TMSessionModel *sessionModel = [self getSessionModel:dataTask.taskIdentifier];
// 寫入數(shù)據(jù)
[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);
}
下載完成后關(guān)閉流屑墨,并從任務(wù)列表中刪除躁锁。
- (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);
}
// 關(guān)閉流
[sessionModel.stream close];
sessionModel.stream = nil;
// 清除任務(wù)
[self.tasks removeObjectForKey:TMFileName(sessionModel.url)];
[self.sessionModels removeObjectForKey:@(task.taskIdentifier).stringValue];
}