欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

總結iOS開發(fā)中的斷點續(xù)傳與實踐

 更新時間:2016年07月27日 09:09:58   投稿:daisy  
本文先從斷點續(xù)傳問題開始,介紹斷點續(xù)傳概述和原理。接著結合筆者調研中嘗試的 AFHTTPRequestOpeartion,簡單分析源碼。最后分別基于 NSURLConnection,NSURLSessionDataTask 和 NSURLSessionDownloadTask 去實現應用重啟情況下的斷點續(xù)傳。下面一起來看看。

前言

斷點續(xù)傳概述

斷點續(xù)傳就是從文件上次中斷的地方開始重新下載或上傳數據,而不是從文件開頭。(本文的斷點續(xù)傳僅涉及下載,上傳不在討論之內)當下載大文件的時候,如果沒有實現斷點續(xù)傳功能,那么每次出現異?;蛘哂脩糁鲃拥臅和?,都會去重頭下載,這樣很浪費時間。所以項目中要實現大文件下載,斷點續(xù)傳功能就必不可少了。當然,斷點續(xù)傳有一種特殊的情況,就是 iOS 應用被用戶 kill 掉或者應用 crash,要實現應用重啟之后的斷點續(xù)傳。這種特殊情況是本文要解決的問題。

斷點續(xù)傳原理

要實現斷點續(xù)傳 , 服務器必須支持。目前最常見的是兩種方式:FTP 和 HTTP

下面來簡單介紹 HTTP 斷點續(xù)傳的原理。

HTTP

通過 HTTP,可以非常方便的實現斷點續(xù)傳。斷點續(xù)傳主要依賴于 HTTP 頭部定義的 Range 來完成。在請求某范圍內的資源時,可以更有效地對大資源發(fā)出請求或從傳輸錯誤中恢復下載。有了 Range,應用可以通過 HTTP 請求曾經獲取失敗的資源的某一個返回或者是部分,來恢復下載該資源。當然并不是所有的服務器都支持 Range,但大多數服務器是可以的。Range 是以字節(jié)計算的,請求的時候不必給出結尾字節(jié)數,因為請求方并不一定知道資源的大小。

Range 的定義如圖 1 所示:

圖 1. HTTP-Range

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

圖 2. HTTP request 例子

在上面的例子中的“Range: bytes=1208765-”表示請求資源開頭 1208765 字節(jié)之后的部分。

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

圖 3. HTTP response 例子

上面例子中的”Accept-Ranges: bytes”表示服務器端接受請求資源的某一個范圍,并允許對指定資源進行字節(jié)類型訪問。”Content-Range: bytes 1208765-20489997/20489998”說明了返回提供了請求資源所在的原始實體內的位置,還給出了整個資源的長度。這里需要注意的是 HTTP return code 是 206 而不是 200。

斷點續(xù)傳分析 -AFHTTPRequestOperation

了解了斷點續(xù)傳的原理之后,我們就可以動手來實現 iOS 應用中的斷點續(xù)傳了。由于筆者項目的資源都是部署在 HTTP 服務器上 , 所以斷點續(xù)傳功能也是基于 HTTP 實現的。首先來看下第三方網絡框架 AFNetworking 中提供的實現。清單 1 示例代碼是用來實現斷點續(xù)傳部分的代碼:

清單 1. 使用 AFHTTPRequestOperation 實現斷點續(xù)傳的代碼
 

// 1 指定下載文件地址 URLString 
 // 2 獲取保存的文件路徑 filePath 
 // 3 創(chuàng)建 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) { 
    // 若下載過 , 斷點續(xù)傳的時候修改 HTTP 頭部部分的 Range 
    NSMutableURLRequest *mutableURLRequest = [request mutableCopy]; 
    NSString *requestRange = 
    [NSString stringWithFormat:@"bytes=%llu-", downloadedBytes]; 
    [mutableURLRequest setValue:requestRange forHTTPHeaderField:@"Range"]; 
    request = mutableURLRequest; 
  } 
 } 

 // 4 創(chuàng)建 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 當前讀取的字節(jié)數
 // totalBytesRead 讀取的總字節(jié)數 , 包含斷點續(xù)傳之前的
 // totalBytesExpectedToRead 文件總大小
 }]; 

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

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

 }]; 

 // 8 啟動 operation 
 [operation start];

使用以上代碼 , 斷點續(xù)傳功能就實現了,應用重新啟動或者出現異常情況下 , 都可以基于已經下載的部分開始繼續(xù)下載。關鍵的地方就是把已經下載的數據持久化。接下來簡單看下 AFHTTPRequestOperation 是怎么實現的。通過查看源碼 , 我們發(fā)現 AFHTTPRequestOperation 繼承自 AFURLConnectionOperation , 而 AFURLConnectionOperation 實現了 NSURLConnectionDataDelegate 協議。

處理流程如圖 4 所示:

圖 4. AFURLHTTPrequestOperation 處理流程

這里 AFNetworking 為什么采取子線程調異步接口的方式 , 是因為直接在主線程調用異步接口 , 會有一個 Runloop 的問題。當主線程調用 [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES] 時 , 請求發(fā)出之后的監(jiān)聽任務會加入到主線程的 Runloop 中 ,RunloopMode 默認為 NSDefaultRunLoopMode, 這個表示只有當前線程的 Runloop 處理 NSDefaultRunLoopMode 時,這個任務才會被執(zhí)行。而當用戶在滾動 TableView 和 ScrollView 的時候,主線程的 Runloop 處于 NSEventTrackingRunLoop 模式下,就不會執(zhí)行 NSDefaultRunLoopMode 的任務。

另外由于采取子線程調用接口的方式 , 所以這邊的 DownloadProgressBlock,success 和 failure Block 都需要回到主線程來處理。

斷點續(xù)傳實戰(zhàn)

了解了原理和 AFHTTPRequestOperation 的例子之后 , 來看下實現斷點續(xù)傳的三種方式:

NSURLConnection

基于 NSURLConnection 實現斷點續(xù)傳 , 關鍵是滿足 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 目錄下。所以當請求出現異?;驊帽挥脩魵⒌?,都可以通過持久化的中間文件來斷點續(xù)傳。初始化 NSURLConnection 的時候要注意設置 scheduleInRunLoop 為 NSRunLoopCommonModes,不然就會出現進度條 UI 無法更新的現象。

實現效果如圖 6 所示:

圖 6. NSURLConnection 演示

NSURLSessionDataTask

蘋果在 iOS7 開始,推出了一個新的類 NSURLSession, 它具備了 NSURLConnection 所具備的方法,并且更強大。由于通過 NSURLConnection 從 2015 年開始被棄用了,所以讀者推薦基于 NSURLSession 去實現續(xù)傳。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!) 
  } 
 }

區(qū)別在與 didComleteWithError, 它將 NSURLConnection 中的 connection:didFailWithError:

connectionDidFinishLoading: 整合到了一起 , 所以這邊要根據 error 區(qū)分執(zhí)行成功的 Block 和失敗的 Block。

實現效果如圖 8 所示:

圖 8. NSURLSessionDataTask 演示

NSURLSessionDownTask

最后來看下 NSURLSession 中用來下載的類 NSURLSessionDownloadTask,對應的協議是 NSURLSessionDownloadDelegate,如圖 9 所示:

圖 9. NSURLSessionDownloadDelegate 協議

其中在退出 didFinishDownloadingToURL 后,會自動刪除 temp 目錄下對應的文件。所以有關文件操作必須要在這個方法里面處理。之前筆者曾想找到這個 tmp 文件 , 基于這個文件做斷點續(xù)傳 , 無奈一直找不到這個文件的路徑。等以后 SWIFT 公布 NSURLSession 的源碼之后,興許會有方法找到?;?NSURLSessionDownloadTask 來實現的話 , 需要在 cancelByProducingResumeData 中保存已經下載的數據。進度通知就非常簡單了,直接在 URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite: 實現即可。

代碼如清單 4 所示:

清單 4. NSURLSessionDownloadTask 的實現

 //SWIFT 

 //UI 觸發(fā) 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開發(fā)中的斷點續(xù)傳與實踐的全部內容,其實,下載的實現遠不止這些內容,本文只介紹了簡單的使用。希望在進一步的學習和應用中能繼續(xù)與大家分享。希望本文能幫助到有需要的大家。

相關文章

  • iOS開發(fā)學習 ViewController使用示例詳解

    iOS開發(fā)學習 ViewController使用示例詳解

    這篇文章主要為大家介紹了iOS開發(fā)學習 ViewController使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • iOS Xcode升級Xcode15報錯SDK does not contain 'libarclite'

    iOS Xcode升級Xcode15報錯SDK does not contain

    這篇文章主要為大家介紹了iOS Xcode 升級Xcode15報錯: SDK does not contain 'libarclite'解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • iOS實現簡單的抽屜效果

    iOS實現簡單的抽屜效果

    這篇文章主要為大家詳細介紹了iOS實現簡單的抽屜效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-02-02
  • iOS高仿微信相冊界面翻轉過渡動畫效果

    iOS高仿微信相冊界面翻轉過渡動畫效果

    在圖片界面點擊右下角的查看評論會翻轉到評論界面,評論界面點擊左上角的返回按鈕會反方向翻轉回圖片界面,真正的實現方法,與傳統(tǒng)的導航欄過渡其實只有一行代碼的區(qū)別,下面小編通過本文給大家介紹下ios高仿微信相冊界面翻轉過渡動畫效果,一起看看吧
    2016-11-11
  • IOS開發(fā)中NSURL的基本操作及用法詳解

    IOS開發(fā)中NSURL的基本操作及用法詳解

    NSURL其實就是我們在瀏覽器上看到的網站地址,這不就是一個字符串么,為什么還要在寫一個NSURL呢,主要是因為網站地址的字符串都比較復雜,包括很多請求參數,這樣在請求過程中需要解析出來每個部門,所以封裝一個NSURL,操作很方便
    2015-12-12
  • iOS中的集合該如何弱引用對象示例詳解

    iOS中的集合該如何弱引用對象示例詳解

    這篇文章主要給大家介紹了關于在iOS中的集合該如何弱引用對象的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來依稀學習學習吧。
    2017-09-09
  • Objective-C實現身份證驗證的方法示例

    Objective-C實現身份證驗證的方法示例

    這篇文章主要給大家分享了Objective-C實現身份證驗證的方法,文中給出了詳細的示例代碼,對大家具有一定的參考價值,需要的朋友們下面來一起看看吧。
    2017-03-03
  • 清除WKWebView cookies的方法

    清除WKWebView cookies的方法

    下面小編就為大家?guī)硪黄宄齏KWebView cookies的方法。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • iOS圖片放大的方式(transform和frame)

    iOS圖片放大的方式(transform和frame)

    這篇文章主要介紹了iOS圖片放大的兩種方式,transform方式放大圖片,從中心開始放大,另一種用frame改變寬高,詳細內容請參考下文
    2016-04-04
  • iOS開發(fā)存儲應用程序Info.plist知識全面詳解

    iOS開發(fā)存儲應用程序Info.plist知識全面詳解

    這篇文章主要為大家介紹了iOS開發(fā)存儲應用程序Info.plist知識全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06

最新評論