Retrofit 2.0-resumable download of large files and resumable download
ApiService
Write the API and execute the download interface function.
public interface ApiService { @Streaming @GET Observable
downloadFile(@Url String fileUrl);}
Because the url is variable@ URL annotation symbol to specify, which is officially recommended for large files@ Streaming for annotation. Otherwise, IO exceptions may occur, and small files can be ignored without injection. If you want to perform resumable upload, you can add the header here, but it is not recommended to write it to the api directly. The size of each download request is different and it is better to add the interceptor.
DownLoadManager
Implements a download manager for file writing and type judgment. If you want to improve it, you can determine the http request header.Content-length,Content-type,RANGE: The first file is used to determine the file size, the second file type, and the third is where the file starts to be downloaded. If you verify the download source, you can addReferer. if the source is not the target, the download permission is not granted.
Public class DownLoadManager {private static final String TAG = "DownLoadManager"; private static String APK_CONTENTTYPE = "application/vnd. android. package-archive "; private static String PNG_CONTENTTYPE =" image/png "; private static String JPG_CONTENTTYPE =" image/jpg "; private static String fileSuffix = ""; public static boolean writeResponseBodyToDisk (Context context, ResponseBody body) {Log. d (T AG, "contentType: >>>" + body. contentType (). toString (); String type = body. contentType (). toString (); if (type. equals (APK_CONTENTTYPE) {fileSuffix = ". apk ";} else if (type. equals (PNG_CONTENTTYPE) {fileSuffix = ". png ";} // others are added to the String path = context. getExternalFilesDir (null) + File. separator + System. currentTimeMillis () + fileSuffix; Log. d (TAG, "path: >>>" + path); try {// todo change t He file location/name according to your needs File futureStudioIconFile = new File (path); InputStream inputStream = null; OutputStream outputStream = null; try {byte [] fileReader = new byte [4096]; long fileSize = body. contentLength (); long fileSizeDownloaded = 0; inputStream = body. byteStream (); outputStream = new FileOutputStream (futureStudioIconFile); while (true) {int read = inputStream. read (FileReader); if (read =-1) {break;} outputStream. write (fileReader, 0, read); fileSizeDownloaded + = read; Log. d (TAG, "file download:" + fileSizeDownloaded + "of" + fileSize);} outputStream. flush (); return true;} catch (IOException e) {return false;} finally {if (inputStream! = Null) {inputStream. close ();} if (outputStream! = Null) {outputStream. close () ;}} catch (IOException e) {return false ;}
}
The above is just a simple judgment on different types of files. For other types, referThe http protocol description type is added by the user. resumable data transfer is not implemented yet. There are many download introductions Based on HttpClinet. The key idea is to recordRANGE to the database. Each row to be downloaded corresponds to one piece of data. The download ID is a unique primary key, includingFilename,FileSize, fileType, downTime, downRangeThe status can basically meet the multi-thread data download requirements. After the program fit is combined with RXJava, the thread security problem is solved. To achieve multi-thread download, the so yi z will be used. I will not do much here.
Call download
OkHttpClient okHttpClient = new OkHttpClient.Builder() .build(); Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .baseUrl(url) .build();Api Service apiService = retrofit.create(ApiService.class);apiService..download(url1, new Subscriber () { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(ResponseBody responseBody) { if (DownLoadManager.writeResponseBodyToDisk(MainActivity.this, responseBody)) { Toast.makeText(MainActivity.this, "Download is sucess", Toast.LENGTH_LONG).show(); } } }); } });
WhenHowever, we can add the progress of the last download to the header to achieve the basic breakpoint download. Currently, the multi-thread download is mainly implemented by the following ideas in the industry, when we request the total length of the file, we can divide the Three-segment size interval to enable three new threads to download the file. After the download is complete, we can use a unique file ID, you can append the three files to a single file. Of course, you do not need to enable a new thread when using retrofit in combination with RxJava. You just need to Subscriber the Subscriber to the IO thread, note that you need to determine the final file size and HashCode to verify the file integrity. Otherwise, there is no big difference between video file packet loss and the maximum frame is stuck, however, the apk packet is lost but cannot be parsed and installed.
The breakpoint download posture is enabled!
Breakpoint download
apiServiece
@GET@StreamingObservable > download(@Header("Range") String range, @Url String url);
API
** DownLoadInfo ** s contains name, lenth,. url, and so on. It will not be pasted here. View the API directly
DownloadTypeThe records must include stats, mLastModify, downloded, savepath, etc.
public Observable download(@NonNull final String url, @NonNull final String saveName, @Nullable final String savePath) {return downloadDispatcher(url, saveName, savePath);}private Observable downloadDispatcher(final String url, final String saveName, final String savePath) { return getDownloadType(url) .flatMap(new Func1 >() { @Override public Observable call(DownloadType type) { try { type.prepare(); } catch (IOException | ParseException e) { return Observable.error(e); } try { return type.start(); } catch (IOException e) { return Observable.error(e); } } }) .doOnCompleted(new Action0() { @Override public void call() { mDownloadManager.delete(url); } }) .doOnError(new Action1 () { @Override public void call(Throwable throwable) { mDownloadManager.delete(url); } }) .doOnUnsubscribe(new Action0() { @Override public void call() { mDownloadManager.delete(url) });}
Start download:
@Override Observable start() { Log.i(TAG, "Normal download start!!"); return mDownloadHelper.getDownloadApi().download(null, mUrl) .subscribeOn(Schedulers.io()) .flatMap(new Func1 , Observable >() { @Override public Observable call(final Response response) { return normalSave(response); } }).onBackpressureLatest().retry(new Func2 () { @Override public Boolean call(Integer integer, Throwable throwable) { return mDownloadHelper.retry(integer, throwable); } }); }
Call
DownloadAgenti.getInstance(mContext) .observeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber () { @Override public void onCompleted() { // todo } @Override public void onError(Throwable e) { todo } @Override public void onNext(final DownloadStatus status) { todo } });