iOS開發網路篇之檔案下載、大檔案下載、斷點下載

發表於2015-09-11

這裡寫圖片描述

iOS開發中經常會用到檔案的下載與上傳功能,今天我們們來分享一下檔案下載的思路。檔案上傳下篇再說。

檔案下載分為小檔案下載與大檔案下載

小檔案下載

小檔案可以是一張圖片,或者一個檔案,這裡指在現行的網路狀況下基本上不需要等待很久就能下載好的檔案。這裡以picjumbo裡的一張圖片為例子。

NSData方式

其實我們經常用的 [NSData dataWithContentsOfURL] 就是一種檔案下載方式,猜測這裡面應該是傳送了Get請求。

當然下載程式碼應該放到子執行緒執行

NSURLConnection方式下載

就是傳送一個非同步的Get請求,回撥的data就是我們下載到的圖片。這些都很簡單,今天主要說的是大檔案的下載。

大檔案下載

NSURLConnection下載

通過上面的兩個方法去下載大檔案是不合理的,因為這兩個方法都是一次性返回整個下載到的檔案,返回的data在記憶體中,如果下載一個幾百兆的東西,記憶體肯定會爆的。其實NSURLConnection還提供了另外一種傳送請求的方式

這裡用到了代理,那肯定要遵守協議了.遵守 NSURLConnectionDataDelegate 協議.

進去看看有幾個代理方法,其實我們能用到的也就三個。

 

通過執行下載操作,分別log上面三個方法,會發現didReceiveData這個方法會被頻繁的呼叫,每次都會傳回來一部分data,下面是官方api對這個方法的說明

is called with a single immutable NSData object to the delegate,representing the next portion of the data loaded from the connection. This is the only guaranteed for the delegate to receive the data from the resource load.

由此我們可以知道,這種下載方式是通過這個代理方法每次傳回來一部分檔案,最終我們把每次傳回來的資料合併成一個我們需要的檔案。

這時候我們通常想到的方法是定義一個全域性的NSMutableData,接受到響應的時候初始化這個MutableData,在didReceiveData方法裡面去拼接

[self.totalData appendData:data];

最後在完成下載的方法裡面吧整個MutableData寫入沙盒。

程式碼如下:

 

我這裡下載的是javajdk。(度孃的地址)

注意:通常大檔案下載是需要給使用者展示下載進度的。

這個數值是 已經下載的資料大小/要下載的檔案總大小

已經下載的資料我們可以記錄,要下載的檔案總大小在伺服器返回的響應頭裡面可以拿到,在接受到響應的方法裡執行

不得不說蘋果太為開發者考慮了,我們不必這麼麻煩的去獲取檔案總大小了,

response.expectedContentLength 這句程式碼就搞定了。

response.suggestedFilename 這句代表獲取下載的檔名

這裡寫圖片描述

題外話扯的有點多,言歸正傳,這樣我們確實可以下載檔案,最後拿到的檔案也能正常執行

這裡寫圖片描述

但是有個致命的問題,記憶體!用來接受檔案的NSMutableData一直都在記憶體中,會隨著檔案的下載一直變大,

這裡寫圖片描述

所有這種處理方式絕對是不合理的。

合理的方式在我們獲取一部分data的時候就寫入沙盒中,然後釋放記憶體中的data。

這裡要用到NSFilehandle這個類,這個類可以實現對檔案的讀取、寫入、更新。

下面總結了一些常用的NSFileHandle的方法,在這個表中,fh是一個NSFileHandle物件,data是一個NSData物件,path是一個NSString 物件,offset是易額Unsigned long long變數。

這裡寫圖片描述

具體關於NSFileHandle的用法各位自行搜尋。

在接受到響應的時候就在沙盒中建立一個空的檔案,然後每次接收到資料的時候就拼接到這個檔案的最後面,通過 - (unsigned long long)seekToEndOfFile; 這個方法

這樣在下載過程中記憶體就會一直很穩定了,並且下載的檔案也是沒問題的。

這裡寫圖片描述

斷點下載

暫停/繼續下載也是現在下載中必備的功能了,如果沒有暫停功能,使用者體驗相比會很差,而且如果突然網路不好中斷了,沒有實現斷點下載的話只有重新下了。。。

下面讓我們來加入斷點下載功能吧。

NSURLConnection 只提供了一個cancel方法,這並不是暫停,而是取消下載任務。如果要實現斷點下載必須要了解HTTP協議中請求頭的Range。

這裡寫圖片描述

不難看出,通過設定請求頭的Range我們可以指定下載的位置、大小。

那麼我們這樣設定 bytes=500- 從500位元組以後的所有位元組 ,

只需要在didReceiveData中記錄已經寫入沙盒中檔案的大小(self.currentLength),

把這個大小設定到請求頭中,因為第一次下載肯定是沒有執行過didReceive方法,self.currentLength也就為0,也就是從頭開始下。

 

在下載過程中,為了提高效率,充分利用cpu效能,通常會執行多執行緒下載,程式碼就不貼了,分析一下思路:

下載開始,建立一個和要下載的檔案大小相同的檔案(如果要下載的檔案為100M,那麼就在沙盒中建立一個100M的檔案,然後計算每一段的下載量,開啟多條執行緒下載各段的資料,分別寫入對應的檔案部分)。

NSURLSession下載方式

上面這種下載檔案的方式確實比較複雜,要自己去控制記憶體寫入相應的位置,不過在蘋果在iOS7推出了一個新的類 NSURLSession ,它具備了NSURLConnection所具備的方法,同時也比它更強大。蘋果推出它的目的大有取代NSURLConnection的趨勢或者目的。

NSURLSession 也可以傳送Get/Post請求,實現檔案的下載和上傳。

在NSURLSesiion中,任何請求都可以被看做是一個任務。其中有三種任務型別

// NSURLSessionDataTask : 普通的GET\POST請求

// NSURLSessionDownloadTask : 檔案下載

// NSURLSessionUploadTask : 檔案上傳(很少用,一般伺服器不支援)

NSURLSession 簡單使用

NSURLSession傳送請求非常簡單,與connection不同的是,任務建立後不會自動傳送請求,需要手動開始執行任務。

NSURLSession 下載

使用NSURLSession就非常簡單了,不需要去考慮什麼邊下載邊寫入沙盒的問題,蘋果都幫我們做好了。程式碼如下

是不是跟NSURLConnection很像,但仔細看會發現回撥的方法裡面並沒用NSData傳回來,多了一個location,顧名思義,location就是下載好的檔案寫入沙盒的地址,列印一下發現下載好的檔案被自動寫入的temp資料夾下面了。

location:file:///Users/yeaodong/Library/Developer/CoreSimulator/Devices/E52B4B95-53E1-46A2-9881-8C969958FBC0/data/Containers/Data/Application/BFB9F0CA-0F50-4682-BBBD-B71B54C39EBE/tmp/CFNetworkDownload_YNnuIS.tmp

這裡寫圖片描述

不過在下載完成之後會自動刪除temp中的檔案,所有我們需要做的只是在回撥中把檔案移動(或者複製,反正之後會自動刪除)到caches中。

不過通過這種方式下載有個缺點就是無法監聽下載進度,要監聽下載進度,蘋果通常的作法是通過delegate,這裡也一樣。而且NSURLSession的建立方式也有所不同。

首先遵守協議 <NSURLSessionDownloadDelegate> 注意不要寫錯

點進去發現協議裡面有三個方法。

 

這上面的註釋已經很詳細了,相信大家都能看懂吧。

NSURLSession建立方式,這裡就不能使用Block回撥方式了,如果給下載任務設定了completionHandler這個block,也實現了下載的代理方法,優先執行block,代理方法也就不會執行了。

相比之前的NSURLConnection方式簡單很多吧,用NSURLSessionDownloadTask做斷點下載也很簡單,我們先了解一下任務的取消方法

取消操作以後會呼叫一個Block,並傳入一個resumeData,該引數包含了繼續下載檔案的位置資訊。也就是說,當你下載了10M得檔案資料,暫停了。那麼你下次繼續下載的時候是從第10M這個位置開始的,而不是從檔案最開始的位置開始下載。因而為了儲存這些資訊,所以才定義了這個NSData型別的這個屬性:resumeData。這個data只包含了url跟已經下載了多少資料,不會很大,不用擔心記憶體問題。

另外,session還提供了通過resumeData來建立任務的方法

我們只需要在取消操作的回撥中記錄好resumeData,然後在恢復下載的適合通過上面的方法建立任務就好了,相比NSURLconnection簡單太多了。需要注意的是Block中迴圈引用的問題

示例程式下載: https://github.com/hongfenglt/HFDownLoad

這篇部落格斷斷續續寫了兩三天,可能某些地方思路有些亂,歡迎大神指正。

 

相關文章