Earlier we already know how to download files using OkHttp and Retrofit .
When downloading files, you may encounter some unexpected situations, such as a network error or the user has suspended the download.

Start the download again, if you want to start from the beginning, it will waste the previously downloaded content. The breakpoint resume function can continue to download files from where it left off.

Http range request

Range is a request header that tells the server which part of the file to return.

In a Range header, you can request multiple sections at once, and the server returns it as a multipart file.

If the server returns a range response, you need to use the 206 Partial Content status code.

If the requested range is not valid, the server will return a 416 Range Not Satisfiable status code indicating a client error.

The server allows the Range header to be ignored, thus returning the entire file with a status code of 200.

For example:

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>

When a request is made, the content of the general Range is written in the form of bytes=0-100. Or specify multiple ranges when requesting multiple parts.

Range: bytes=200-1000, 2000-6576, 19000-

Content-Range represents the length or size of the body.

reference:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range

Use example of OkHttp and Retrofit

Use the OkHttp and Retrofit to add the Range header to tell the server which range of file data we need.

The method defined requires the input @Header(“Range”)

Private  interface  ApiService  { @Streaming @GET Observable<ResponseBody> downloadPartial (@Url String url, @Header( "Range" ) String range) ; }

Need to pass the Range string as 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 () {
           
        } 
    });

We can also check the size of the downloaded part of the file before downloading, and then determine the range of Range. When resuming, write to the local file and note the append mode of the stream.

Fos = new FileOutputStream(file, true );