你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> 總結iOS開辟中的斷點續傳與理論

總結iOS開辟中的斷點續傳與理論

編輯:IOS開發綜合

媒介

斷點續傳概述

斷點續傳就是從文件前次中止的處所開端從新下載或上傳數據,而不是從文件開首。(本文的斷點續傳僅觸及下載,上傳不在評論辯論以內)當下載年夜文件的時刻,假如沒有完成斷點續傳功效,那末每次湧現異常或許用戶自動的暫停,都邑去重頭下載,如許很糟蹋時光。所以項目中要完成年夜文件下載,斷點續傳功效就必弗成少了。固然,斷點續傳有一種特別的情形,就是 IOS 運用被用戶 kill 失落或許運用 crash,要完成運用重啟以後的斷點續傳。這類特別情形是本文要處理的成績。

斷點續傳道理

要完成斷點續傳 , 辦事器必需支撐。今朝最多見的是兩種方法:FTP 和 HTTP。

上面來簡略引見 HTTP 斷點續傳的道理。

HTTP

經由過程 HTTP,可以異常便利的完成斷點續傳。斷點續傳重要依附於 HTTP 頭部界說的 Range 來完成。在要求某規模內的資本時,可以更有用地對年夜資本收回要求或從傳輸毛病中恢復下載。有了 Range,運用可以經由過程 HTTP 要求已經獲得掉敗的資本的某一個前往或許是部門,來恢復下載該資本。固然其實不是一切的辦事器都支撐 Range,但年夜多半辦事器是可以的。Range 是以字節盤算的,要求的時刻不用給出開頭字節數,由於要求方其實不必定曉得資本的年夜小。

Range 的界說如圖 1 所示:

圖 1. HTTP-Range

圖 2 展現了 HTTP request 的頭部信息:

圖 2. HTTP request 例子

在下面的例子中的“Range: bytes=1208765-”表現要求資本開首 1208765 字節以後的部門。

圖 3 展現了 HTTP response 的頭部信息:

圖 3. HTTP response 例子

下面例子中的”Accept-Ranges: bytes”表現辦事器端接收要求資本的某一個規模,並許可對指定資本停止字節類型拜訪。”Content-Range: bytes 1208765-20489997/20489998”解釋了前往供給了要求資本地點的原始實體內的地位,還給出了全部資本的長度。這裡須要留意的是 HTTP return code 是 206 而不是 200。

斷點續傳剖析 -AFHTTPRequestOperation

懂得了斷點續傳的道理以後,我們便可以著手來完成 IOS 運用中的斷點續傳了。因為筆者項目標資本都是安排在 HTTP 辦事器上 , 所以斷點續傳功效也是基於 HTTP 完成的。起首來看下第三方收集框架 AFNetworking 中供給的完成。清單 1 示例代碼是用來完成斷點續傳部門的代碼:

清單 1. 應用 AFHTTPRequestOperation 完成斷點續傳的代碼
 

// 1 指定下載文件地址 URLString 
 // 2 獲得保留的文件途徑 filePath 
 // 3 創立 NSURLRequest 
 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]]; 
 unsigned long long downloadedBytes = 0; 

 if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { 
 // 3.1 若之前下載過 , 則在 HTTP 要求頭部參加 Range 
  // 獲得已下載文件的 size 
  downloadedBytes = [self fileSizeForPath:filePath]; 

  // 驗證能否下載過文件
  if (downloadedBytes > 0) { 
    // 若下載過 , 斷點續傳的時刻修正 HTTP 頭部部門的 Range 
    NSMutableURLRequest *mutableURLRequest = [request mutableCopy]; 
    NSString *requestRange = 
    [NSString stringWithFormat:@"bytes=%llu-", downloadedBytes]; 
    [mutableURLRequest setValue:requestRange forHTTPHeaderField:@"Range"]; 
    request = mutableURLRequest; 
  } 
 } 

 // 4 創立 AFHTTPRequestOperation 
 AFHTTPRequestOperation *operation 
 = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 

 // 5 設置操作輸入流 , 保留在第 2 步的文件中
 operation.outputStream = [NSOutputStream 
 outputStreamToFileAtPath:filePath append:YES]; 

 // 6 設置下載進度處置 block 
 [operation setDownloadProgressBlock:^(NSUInteger bytesRead, 
 long long totalBytesRead, long long totalBytesExpectedToRead) { 
 // bytesRead 以後讀取的字節數
 // totalBytesRead 讀取的總字節數 , 包括斷點續傳之前的
 // totalBytesExpectedToRead 文件總年夜小
 }]; 

 // 7 設置 success 和 failure 處置 block 
 [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation 
 *operation, id responSEObject) { 

 } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 

 }]; 

 // 8 啟動 operation 
 [operation start];

應用以上代碼 , 斷點續傳功效就完成了,運用從新啟動或許湧現異常情形下 , 都可以基於曾經下載的部門開端持續下載。症結的處所就是把曾經下載的數據耐久化。接上去簡略看下 AFHTTPRequestOperation 是怎樣完成的。經由過程檢查源碼 , 我們發明 AFHTTPRequestOperation 繼續自 AFURLConnectionOperation , 而 AFURLConnectionOperation 完成了 NSURLConnectionDataDelegate 協定。

處置流程如圖 4 所示:

圖 4. AFURLHTTPrequestOperation 處置流程

這裡 AFNetworking 為何采用子線程調異步接口的方法 , 是由於直接在主線程挪用異步接口 , 會有一個 Runloop 的成績。當主線程挪用 [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES] 時 , 要求收回以後的監聽憑務會參加到主線程的 Runloop 中 ,RunloopMode 默許為 NSDefaultRunLoopMode, 這個表現只要以後線程的 Runloop 處置 NSDefaultRunLoopMode 時,這個義務才會被履行。而當用戶在轉動 TableView 和 ScrollView 的時刻,主線程的 Runloop 處於 NSEventTrackingRunLoop 形式下,就不會履行 NSDefaultRunLoopMode 的義務。

別的因為采用子線程挪用接口的方法 , 所以這邊的 DownloadProgressBlock,success 和 failure Block 都須要回到主線程來處置。

斷點續傳實戰

懂得了道理和 AFHTTPRequestOperation 的例子以後 , 來看下完成斷點續傳的三種方法:

NSURLConnection

基於 NSURLConnection 完成斷點續傳 , 症結是知足 NSURLConnectionDataDelegate 協定,重要完成了以下三個辦法:

清單 2. NSURLConnection 的完成

 // SWIFT 
 // 要求掉敗處置
 func connection(connection: NSURLConnection, 
 didFailWithError error: NSError) { 
  self.failureHandler(error: error) 
 } 

 // 吸收到辦事器呼應是挪用
 func connection(connection: NSURLConnection, 
 didReceiveResponse response: NSURLResponse) { 
  if self.totalLength != 0 { 
    return 
  } 

  self.writeHandle = NSFileHandle(forWritingAtPath: 
  FileManager.instance.cacheFilePath(self.fileName!)) 

  self.totalLength = response.expectedContentLength + self.currentLength 
 } 

 // 當辦事器前往實體數據是挪用
 func connection(connection: NSURLConnection, didReceiveData data: NSData) { 
  let length = data.length 

  // move to the end of file 
  self.writeHandle.seekToEndOfFile() 

  // write data to sanbox 
  self.writeHandle.writeData(data) 

  // calculate data length 
  self.currentLength = self.currentLength + length 

  print("currentLength\(self.currentLength)-totalLength\(self.totalLength)") 

  if (self.downloadProgressHandler != nil) { 
    self.downloadProgressHandler(bytes: length, totalBytes: 
    self.currentLength, totalBytesExpected: self.totalLength) 
  } 
 } 

 // 下載終了後挪用
 func connectionDidFinishLoading(connection: NSURLConnection) { 
  self.currentLength = 0 
  self.totalLength = 0 

  //close write handle 
  self.writeHandle.closeFile() 
  self.writeHandle = nil 

  let cacheFilePath = FileManager.instance.cacheFilePath(self.fileName!) 
  let documenFilePath = FileManager.instance.documentFilePath(self.fileName!) 

  do { 
    try FileManager.instance.moveItemAtPath(cacheFilePath, toPath: documenFilePath) 
  } catch let e as NSError { 
    print("Error occurred when to move file: \(e)") 
  } 

  self.successHandler(responSEObject:fileName!) 
 }

如圖 5 所示 , 解釋了 NSURLConnection 的普通處置流程。

圖 5. NSURLConnection 流程

依據圖 5 的普通流程,在 didReceiveResponse 中初始化 fileHandler, 在 didReceiveData 中 , 將吸收到的數據耐久化的文件中 , 在 connectionDidFinishLoading 中,清空數據和封閉 fileHandler,並將文件保留到 Document 目次下。所以當要求湧現異常或運用被用戶殺失落,都可以經由過程耐久化的中央文件來斷點續傳。初始化 NSURLConnection 的時刻要留意設置 scheduleInRunLoop 為 NSRunLoopCommonModes,否則就會湧現進度條 UI 沒法更新的景象。

完成後果如圖 6 所示:

圖 6. NSURLConnection 演示

NSURLSessionDataTask

蘋果在 IOS7 開端,推出了一個新的類 NSURLSession, 它具有了 NSURLConnection 所具有的辦法,而且更壯大。因為經由過程 NSURLConnection 從 2015 年開端被棄用了,所以讀者推舉基於 NSURLSession 去完成續傳。NSURLConnection 和 NSURLSession delegate 辦法的映照關系 , 如圖 7 所示。所以症結是要知足 NSURLSessionDataDelegate 和 NSURLsessionTaskDelegate。

圖 7. 協定之間映照關系

代碼如清單 3 所示 , 根本和 NSURLConnection 完成的一樣。

清單 3. NSURLSessionDataTask 的完成

 // SWIFT 
 // 吸收數據
 func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, 
 idReceiveData data: NSData) { 
  //. . . 
 } 
 // 吸收辦事器呼應
 func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, 
 didReceiveResponse response: NSURLResponse, completionHandler: 
 (NSURLSessionResponseDisposition) -> Void) { 
  // . . . 
  completionHandler(.Allow) 
 } 

 // 要求完成
 func URLSession(session: NSURLSession, task: NSURLSessionTask, 
 didCompleteWithError error: NSError?) { 
  if error == nil { 
    // . . . 
    self.successHandler(responSEObject:self.fileName!) 
  } else { 
    self.failureHandler(error:error!) 
  } 
 }

差別在與 didComleteWithError, 它將 NSURLConnection 中的 connection:didFailWithError:

和 connectionDidFinishLoading: 整合到了一路 , 所以這邊要依據 error 辨別履行勝利的 Block 和掉敗的 Block。

完成後果如圖 8 所示:

圖 8. NSURLSessionDataTask 演示

NSURLSessionDownTask

最初來看下 NSURLSession 頂用來下載的類 NSURLSessionDownloadTask,對應的協定是 NSURLSessionDownloadDelegate,如圖 9 所示:

圖 9. NSURLSessionDownloadDelegate 協定

個中在加入 didFinishDownloadingToURL 後,會主動刪除 temp 目次下對應的文件。所以有關文件操作必需要在這個辦法外面處置。之前筆者曾想找到這個 tmp 文件 , 基於這個文件做斷點續傳 , 無法一向找不到這個文件的途徑。等今後 SWIFT 頒布 NSURLSession 的源碼以後,也許會無方法找到。基於 NSURLSessionDownloadTask 來完成的話 , 須要在 cancelByProducingResumeData 中保留曾經下載的數據。進度告訴就異常簡略了,直接在 URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite: 完成便可。

代碼如清單 4 所示:

清單 4. NSURLSessionDownloadTask 的完成

 //SWIFT 

 //UI 觸發 pause 
 func pause(){ 
  self.downloadTask?.cancelByProducingResumeData({data -> Void in 
    if data != nil { 
 data!.writeToFile(FileManager.instance.cacheFilePath(self.fileName!), 
 atomically: false) 
 } 
    }) 
  self.downloadTask = nil 
 } 

 // MARK: - NSURLSessionDownloadDelegate 
 func URLSession(session: NSURLSession, downloadTask: 
 NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, 
 totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { 
  if (self.downloadProgressHandler != nil) { 
    self.downloadProgressHandler(bytes: Int(bytesWritten), 
     totalBytes: totalBytesWritten, totalBytesExpected: totalBytesExpectedToWrite) 
  } 
 } 

 func URLSession(session: NSURLSession, task: NSURLSessionTask, 
 didCompleteWithError error: NSError?) { 
  if error != nil {//real error 
    self.failureHandler(error:error!) 
  } 
 } 

 func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, 
 didFinishDownloadingToURL location: NSURL) { 
  let cacheFilePath = FileManager.instance.cacheFilePath(self.fileName!) 
  let documenFilePath = FileManager.instance.documentFilePath(self.fileName!) 
  do { 
    if FileManager.instance.fileExistsAtPath(cacheFilePath){ 
      try FileManager.instance.removeItemAtPath(cacheFilePath) 
    } 
    try FileManager.instance.moveItemAtPath(location.path!, toPath: documenFilePath) 
  } catch let e as NSError { 
    print("Error occurred when to move file: \(e)") 
  } 
  self.successHandler(responseObject:documenFilePath) 
 }

完成後果如圖 10 所示:

圖 10. NSURLSessionDownloadTask 演示

總結

以上就是本文總結iOS開辟中的斷點續傳與理論的全體內容,其實,下載的完成遠不止這些內容,本文只引見了簡略的應用。願望在進一步的進修和運用中能持續與年夜家分享。願望本文能贊助到有須要的年夜家。

【總結iOS開辟中的斷點續傳與理論】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved