Android 断点续传实现原理
轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/120956134
本文出自【趙彥軍的博客】
文章目錄
- 下載原理
- 斷點續傳原理
- 1、java.io.RandomAccessFile
- 2、請求響應碼及Header
- 3、處理請求資源發生改變的問題
- 4、文件可復用判斷
下載原理
在介紹斷點續傳之前,我們先說說下載的原理。代碼示例用 OkHttp 作為示例。
下載核心思路是把 responseBody 寫入文件,核心代碼如下:
但是這種做法有個明顯的問題,假如手機在下載文件的時候下載了80%,某些原因斷網了,如果不支持斷點續傳,那就只有被迫重頭開始下載。但是如果有斷點續傳的加持,就只需要下載最后 20% 的資源,避免重新下載。
斷點續傳原理
1、java.io.RandomAccessFile
斷點續傳/下載需要使用到 java.io.RandomAccessFile 類,RandomAccessFile 的實例支持讀取和寫入隨機訪問文件,它也可以 seek(long pos) 設置從此文件的開頭開始測量的文件指針偏移量,在該位置進行下一次讀取或寫入操作。簡單點說就是可以通過 seek(long pos)方法跳過pos字節開始寫入字節。
如何創建一個 RandomAccessFile ?
//rw:支持可讀可寫 val file = RandomAccessFile(file, "rw")假如我有一段文本要寫入文件,但是需要從文件的第100個字節開始寫,那么需要調用 seek(long pos ) 方法跳過前 100 個字節, 具體實現如下:
val file = RandomAccessFile(file, "rw") file.seek(100) file.writeBytes(.....)所以,我們可以把 responseBody 寫入文件的實現改成如下實現:
2、請求響應碼及Header
正常情況下,我們下載一個文件,響應碼是 200
responseBody 返回的是整個文件的內容。如何才能返回部分內容?
在實現分塊請求之前,首先要判斷服務器是否支持分塊返回,標志是 Access-Ranges ,值為 bytes 就代表服務器支持分塊返回,也就是支持斷點續傳。
所以,我們可以封裝一個方法,來判斷服務是否支持斷點續傳
在確定了服務器支持分塊傳輸后,我們就可以在請求資源的時候,添加 header 請求 Range 字段,來指定請求實體的范圍。它的范圍取值是在 0-Content-Length 之間,使用 - 分割。
例如已經下載了 1000 bytes 的資源內容,想接著繼續下載之后的資源內容,只要在 HTTP 請求頭部,增加 Range:bytes=1000- 就可以了。
Range 還有幾種不同的方式來限定范圍,可以根據需要靈活定制:
- 500-1000:指定開始和結束的范圍。
- 500- :指定開始區間,一直傳遞到結束。這個就比較適用于斷點續傳、或者在線播放等等。
- -500:無開始區間,只意思是需要最后 500 bytes 的內容實體。
- 100-300,1000-3000:指定多個范圍,這種方式使用的場景很少,了解一下就好了。
具體舉例如下:
通過抓包我們發現,添加了 Range 后,請求響應碼變成了 206
并且添加 Range 后,響應的 responseBody 就不再是整個文件的內容了,而是一個片段,我們只需要把這個片段的數據直接寫入文件就可以。
注意事項,如何計算 Range 請求頭的開始值,一般取用緩存文件的長度值,如下
val start = targetFile.length()3、處理請求資源發生改變的問題
在現實的場景中,服務器中的文件是會有發生變化的情況的,那么我們發起續傳的請求肯定是失敗的
那么為了處理這種服務器文件資源發生改變的問題,在 RFC2616 中定義了 Last-Modified和 Etag 來判斷續傳文件資源是否發生改變。
- Last-Modified:記錄 Http 頁面最后修改時間的 Http 頭部參數,Last-Modified 是由服務端發送給客戶端的
- Etag:作為文件的唯一標志,這個標志可以是文件的 hash 值或者是一個版本
我們封裝一個方法用來獲取這兩個值
if-Range:用于判斷實體是否發生改變,如果實體未改變,服務器發送客戶端丟失的部分,否則發送整個實體。一般格式:
If-Range: Etag | HTTP-DateIf-Range 可以使用 Etag 或者 Last-Modified 返回的值。當沒有 ETage 卻有 Last-modified 時,可以把 Last-modified 作為 If-Range 字段的值。實現舉例如下:
關于 Etag 、Last-Modified 注意事項:
- 當第一次請求資源的時候,獲取 Etag/Last-Modified 值,并且保存到本地
- 當下一次需要斷點續傳的時候,把 Etag/Last-Modified 值取出,添加到請求頭 If-Range 字段
4、文件可復用判斷
在文件下載完成后,如果又發起了一次下載請求,那么此時不應該重復下載的。需要做可復用判斷,思路如下
- 1、獲取目前已下載文件的大小
- 2、獲取文件url 獲取服務器文件大小
- 3、如果已下載大小等于服務器文件大小,則復用已下載的文件
舉例如下:
//獲取已下載文件長度 val downloadLength = targetFile.length() //獲取服務器文件長度 val contentLength = responseBody.contentLength() //兩者長度對比 if(downloadLength == contentLength){//復用已下載文件,不再重復下載 }總結
以上是生活随笔為你收集整理的Android 断点续传实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java字符串、文件MD5工具类
- 下一篇: Android Flow遇见Retrof