1. 程式人生 > >iOS-NSURLSessionDataTask實現斷點續傳功能

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,任務會處於暫停狀態,需要你取出任務以及之前下載的程序,重新整理任務下載列表,重新開啟任務。