iOS-NSURLSessionDataTask實現斷點續傳功能
平時專案開發中,經常遇到下載視訊、語音、圖片等等,其中斷點續傳是最常見的,當然這也是根據產品需求而定的,如果檔案很小,就用不到斷點,嗖地一下就下載好了。
斷點續傳可以用蘋果原生的方法,也可以用AFNetworking。
本節先講蘋果原生的檔案下載方法,這裡需要了解NSURLSession:
一、NSURLSession簡介
NSURLConnection在iOS9被宣佈棄用,NSURLSession是蘋果在iOS7後為HTTP資料傳輸提供的一系列介面,比NSURLConnection更強大,更好用。其實不難,今天從使用的角度介紹下。
1.使用NSURLSession,共分兩步:
第一步 通過NSURLSession的例項建立task
第二部 執行task
2.NSURLSessionTask
NSURLSessionTask可以簡單理解為任務:如資料請求任務、下載任務、上傳任務等等。NSURLSessionTask是一個抽象類,其下面有三個子類:NSURLSessionDataTask
NSURLSessionUploadTask
NSURLSessionDownloadTask
從這幾個子類的名字就可以看出他們的作用了。接下來我們就從不同型別的任務出發,來使用session:
3.NSURLSessionDataTask
這個類是和資料相關的任務,但其實dataTask完全可以勝任downloadTask和uploadTask的工作。這可能也是我們使用最多的task種類,而且下面要講的的斷點續傳使用的也是這個子類。
注意:所有型別的task都要呼叫resume方法才會開始進行請求。
task本身有四種狀態,task.status,用來判斷當前任務的狀態:
NSURLSessionTaskStateRunning ///< 正在下載狀態
NSURLSessionTaskStateSuspended ///< 暫停狀態
NSURLSessionTaskStateCanceling ///< 取消任務狀態
NSURLSessionTaskStateCompleted ///< 完成狀態
task本身會有三個方法,結合這三個方法可以實現斷點續傳功能:
// 讓當前的任務暫停,此時任務還可以被resume方法喚醒
- (void)suspend;
// 不僅可以啟動任務,還可以喚醒suspend狀態的任務
- (void)resume;
// 取消當前的任務,你也可以向處於suspend狀態的任務傳送cancel訊息,任務如果被取消便不能再恢復到之前的狀態
- (void)cancel;
4.NSURLSessionConfiguration
NSURLSessionConfiguration屬於session的配置資訊。如:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
// 超時時間
config.timeoutIntervalForRequest = 10;
// 是否允許使用蜂窩網路(後臺傳輸不適用)
config.allowsCellularAccess = YES;
// 還有很多可以設定的屬性
Configuration有三種配置型別,我們通常用的是預設配置:
[NSURLSessionConfiguration defaultSessionConfiguration]
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier
這裡表示了NSURLSession幾種不同的工作模式.
預設的配置會將快取儲存在磁碟上
第二種瞬時會話模式不會建立永續性儲存的快取
第三種後臺會話模式允許程式在後臺進行上傳下載工作.
除了支援任務的暫停和斷點續傳,我覺得NSURLSession之於NSURLConnection的最偉大的進步就是支援後臺上傳下載任務.
二、斷點續傳
1.首先我們使用的是抽象類NSURLSessionTask的一個字類NSURLSessionDataTask進行下載任務。
第一步:建立流和任務設定代理
<NSURLSessionDataDelegate>
@property (nonatomic , strong) NSOutputStream *stream;
@property (nonatomic , strong) NSURLSessionDataTask *task;
@property (nonatomic , assign) NSInteger totalLength;
第二步:建立下載任務請求
// 建立session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 建立流
// 這裡的XXX是視訊流下載後儲存的路徑
self.stream = [NSOutputStream outputStreamToFileAtPath:XXX append:YES];
// 建立請求
// 這裡的url是你的網路視訊URL字串
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
// 建立一個Data任務
self.task = [session dataTaskWithRequest:request];
// 利用KVC修改taskIdentifier的值,這是任務的標識
[task setValue:@(11111) forKeyPath:@"taskIdentifier"];
[self.task resume];
3.NSURLSessionDataDelegate代理方法實現
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
// 開啟流
[self.stream open];
// 獲得伺服器這次請求 返回資料的總長度
self.totalLength = [response.allHeaderFields[@"Content-Length"] integerValue];
// 接收這個請求,允許接收伺服器的資料
completionHandler(NSURLSessionResponseAllow);
}
// 接收到伺服器返回的資料,一直回撥,直到下載完成,暫停時會停止呼叫
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
// 寫入資料
[self.stream write:data.bytes maxLength:data.length];
// 下載進度
NSUInteger receivedSize = [UIUtil getDownloadFileLengthPathWithUrl:@"1122aabb"];
CGFloat progress = 1.0 * receivedSize / self.totalLength;
NSLog(@"%.f%%",progress*100);
}
// 當任務下載完成或失敗會呼叫
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if (error == nil) {
// 下載成功 關閉流,取消任務
[self.stream close];
self.stream = nil;
[self.task cancel];
self.task = nil;
}
}
這時候任務檔案就開始下載了,需要暫停:
[self.task suspend];
繼續下載:
[self.task resume];
到這裡一個完整的斷點續傳就完成了,你會發現這裡沒有用到tmp臨時檔案,如果用子類NSURLSessionDownloadTask,需要你把tmp裡面的下載檔案轉移到制定資料夾才可以。
提示:如果是多工下載,可以利用model儲存每個task,以及每個任務的總長度和已下載長度,一般用到了多工下載都會牽扯到大量的資料儲存,最好用FMDB資料庫(或CoreData)儲存。一般殺死後臺程序後,再進入app,任務會處於暫停狀態,需要你取出任務以及之前下載的程序,重新整理任務下載列表,重新開啟任務。