前面我們已經知道如何使用OkHttp+Retrofit下載文件。
下載文件時,可能會遇到一些意外情況,比如網絡錯誤或是用戶暫停了下載。
再次啟動下載,如果又要從頭開始,會白白浪費前面下載好的內容。
斷點續傳功能可以從上次停止的地方繼續下載文件。
http范圍請求
Range 是一個請求首部,告知服務器返回文件的哪一部分。
在一個 Range 首部中,可以一次性請求多個部分,服務器會以 multipart 文件的形式將其返回。
如果服務器返回的是范圍響應,需要使用 206 Partial Content 狀態碼。
假如所請求的范圍不合法,那么服務器會返回 416 Range Not Satisfiable 狀態碼,表示客戶端錯誤。
服務器允許忽略Range首部,從而返回整個文件,狀態碼用200。
示例
Range: <unit>=<range-start>-
Range: <unit>=<range-start>-<range-end>
Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>
Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>, <range-start>-<range-end>
發起請求時,一般Range的內容寫成 bytes=0-100 這樣的形式。
或者請求多個部分時,指定多個范圍。
Range: bytes=200-1000, 2000-6576, 19000-
Content-Range 表示主體長度或者尺寸。
參考:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Range
使用示例
參考
https://github.com/RustFisher/android-Basic4/tree/master/appdowloadsample
使用OkHttp添加Range頭部,告知服務器我們需要的文件數據范圍。
定義的方法中要求傳入 @Header("Range")
private interface ApiService {
@Streaming
@GET
Observable<ResponseBody> downloadPartial(@Url String url, @Header("Range") String range);
}
需要傳入的Range字符串形如 bytes=200-1000
retrofit.create(ApiService.class)
.downloadPartial(callBack.getUrl(), "bytes=" + startByte + "-")
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.io())
.doOnNext(new Consumer<ResponseBody>() {
@Override
public void accept(ResponseBody responseBody) throws Exception {
callBack.saveFile(responseBody);
}
})
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
tellDownloadError(callBack.getUrl(), throwable);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ResponseBody responseBody) {
}
@Override
public void onError(Throwable e) {
callBack.setState(DownloadTaskState.ERROR);
tellDownloadError(callBack.getUrl(), e);
}
@Override
public void onComplete() {
}
});
我們也可以在下載前,先去檢查文件已下載的部分的大小,再決定Range范圍。
續傳時,寫入本地文件注意選擇流的append模式。
fos = new FileOutputStream(file, true);