123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- package com.benyanyi.okhttp.download;
- import android.content.Context;
- import com.benyanyi.okhttp.listener.OnDownLoadObserver;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.HashMap;
- import java.util.concurrent.atomic.AtomicReference;
- import io.reactivex.Observable;
- import io.reactivex.ObservableEmitter;
- import io.reactivex.ObservableOnSubscribe;
- import io.reactivex.ObservableSource;
- import io.reactivex.android.schedulers.AndroidSchedulers;
- import io.reactivex.functions.Function;
- import io.reactivex.functions.Predicate;
- import io.reactivex.schedulers.Schedulers;
- import okhttp3.Call;
- import okhttp3.OkHttpClient;
- import okhttp3.Request;
- import okhttp3.Response;
- /**
- * @author BenYanYi
- * @date 2018/11/29 15:16
- * @email ben@yanyi.red
- * @overview
- */
- class DownloadManager {
- private static final AtomicReference<DownloadManager> INSTANCE = new AtomicReference<>();
- /**
- * 用来存放各个下载的请求
- */
- private HashMap<String, Call> downCalls;
- /**
- * OKHttpClient;
- */
- private OkHttpClient mClient;
- private Context mContext;
- private String suffix = "";//文件名后缀
- /**
- * 获得一个单例类
- *
- * @param mContext
- * @return
- */
- public static DownloadManager getInstance(Context mContext) {
- for (; ; ) {
- DownloadManager current = INSTANCE.get();
- if (current != null) {
- return current;
- }
- current = new DownloadManager(mContext);
- if (INSTANCE.compareAndSet(null, current)) {
- return current;
- }
- }
- }
- private DownloadManager(Context mContext) {
- downCalls = new HashMap<>();
- mClient = new OkHttpClient.Builder().build();
- this.mContext = mContext;
- }
- /**
- * 开始下载
- *
- * @param suffix 文件名后缀
- * @param url 下载请求的网址
- * @param onDownLoadObserver 用来回调的接口
- */
- void download(String url, String suffix, OnDownLoadObserver onDownLoadObserver) {
- this.suffix = suffix;
- this.download(url, onDownLoadObserver);
- }
- /**
- * 开始下载
- *
- * @param url 下载请求的网址
- * @param onDownLoadObserver 用来回调的接口
- */
- void download(String url, OnDownLoadObserver onDownLoadObserver) {
- Observable.just(url)
- //call的map已经有了,就证明正在下载,则这次不下载
- .filter(new Predicate<String>() {
- @Override
- public boolean test(String s) throws Exception {
- return !downCalls.containsKey(s);
- }
- })
- .flatMap(new Function<String, ObservableSource<?>>() {
- @Override
- public ObservableSource<?> apply(String s) throws Exception {
- return Observable.just(createDownInfo(s));
- }
- })
- //检测本地文件夹,生成新的文件名
- .map(new Function<Object, DownloadInfo>() {
- @Override
- public DownloadInfo apply(Object o) throws Exception {
- return getRealFileName((DownloadInfo) o);
- }
- })
- //下载
- .flatMap(new Function<DownloadInfo, ObservableSource<DownloadInfo>>() {
- @Override
- public ObservableSource<DownloadInfo> apply(DownloadInfo downloadInfo) throws Exception {
- return Observable.create(new DownloadSubscribe(downloadInfo));
- }
- })
- //在主线程回调
- .observeOn(AndroidSchedulers.mainThread())
- //在子线程执行
- .subscribeOn(Schedulers.io())
- //添加观察者
- .subscribe(onDownLoadObserver);
- }
- void cancel(String url) {
- Call call = downCalls.get(url);
- if (call != null) {
- call.cancel();//取消
- }
- downCalls.remove(url);
- }
- /**
- * 创建DownInfo
- *
- * @param url 请求网址
- * @return DownInfo
- */
- private DownloadInfo createDownInfo(String url) {
- DownloadInfo downloadInfo = new DownloadInfo(url);
- //获得文件大小
- long contentLength = getContentLength(url);
- downloadInfo.setTotal(contentLength);
- String fileName = url.substring(url.lastIndexOf("/")) + suffix;
- downloadInfo.setFileName(fileName);
- return downloadInfo;
- }
- private DownloadInfo getRealFileName(DownloadInfo downloadInfo) throws Exception {
- String fileName = downloadInfo.getFileName();
- String savePath = FileUtil.isExistDir(mContext.getPackageName());
- long downloadLength = 0, contentLength = downloadInfo.getTotal();
- File file = new File(savePath, fileName);
- if (file.exists()) {
- //找到了文件,代表已经下载过,则获取其长度
- downloadLength = file.length();
- }
- //之前下载过,需要重新来一个文件
- int i = 1;
- while (downloadLength >= contentLength) {
- int dotIndex = fileName.lastIndexOf(".");
- String fileNameOther;
- if (dotIndex == -1) {
- fileNameOther = fileName + "(" + i + ")";
- } else {
- fileNameOther = fileName.substring(0, dotIndex)
- + "(" + i + ")" + fileName.substring(dotIndex);
- }
- File newFile = new File(savePath, fileNameOther + suffix);
- file = newFile;
- downloadLength = newFile.length();
- i++;
- }
- //设置改变过的文件名/大小
- downloadInfo.setProgress(downloadLength);
- downloadInfo.setFileName(file.getName());
- downloadInfo.setFile(file);
- return downloadInfo;
- }
- private class DownloadSubscribe implements ObservableOnSubscribe<DownloadInfo> {
- private DownloadInfo downloadInfo;
- DownloadSubscribe(DownloadInfo downloadInfo) {
- this.downloadInfo = downloadInfo;
- }
- @Override
- public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {
- String url = downloadInfo.getUrl();
- //已经下载好的长度
- long downloadLength = downloadInfo.getProgress();
- //文件的总长度
- long contentLength = downloadInfo.getTotal();
- //初始进度信息
- e.onNext(downloadInfo);
- Request request = new Request.Builder()
- //确定下载的范围,添加此头,则服务器就可以跳过已经下载好的部分
- .addHeader("RANGE", "bytes=" + downloadLength + "-" + contentLength)
- .url(url)
- .build();
- Call call = mClient.newCall(request);
- //把这个添加到call里,方便取消
- downCalls.put(url, call);
- Response response = call.execute();
- String savePath = FileUtil.isExistDir(mContext.getPackageName());
- File file = new File(savePath, downloadInfo.getFileName());
- InputStream is = null;
- FileOutputStream fileOutputStream = null;
- try {
- is = response.body().byteStream();
- fileOutputStream = new FileOutputStream(file, true);
- //缓冲数组2kB
- byte[] buffer = new byte[2048];
- int len;
- while ((len = is.read(buffer)) != -1) {
- fileOutputStream.write(buffer, 0, len);
- downloadLength += len;
- downloadInfo.setProgress(downloadLength);
- e.onNext(downloadInfo);
- }
- downloadInfo.setFile(file);
- fileOutputStream.flush();
- downCalls.remove(url);
- } finally {
- //关闭IO流
- IoUtil.closeAll(is, fileOutputStream);
- }
- e.onComplete();//完成
- }
- }
- /**
- * 获取下载长度
- *
- * @param downloadUrl
- * @return
- */
- private long getContentLength(String downloadUrl) {
- Request request = new Request.Builder()
- .url(downloadUrl)
- .build();
- try {
- Response response = mClient.newCall(request).execute();
- if (response != null && response.isSuccessful()) {
- long contentLength = response.body().contentLength();
- response.close();
- return contentLength == 0 ? DownloadInfo.TOTAL_ERROR : contentLength;
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return DownloadInfo.TOTAL_ERROR;
- }
- }
|