okHttp3断点续传下载文件

项目中一直用到okHttp3作为网络层,一直想着做一下记录,也一直没写,现在在这里做一下记录。

方法可以直接使用,并且返回下载的进度,当下载中断会生成缓存文件,再次下载相同文件会重新从上次断点处继续下载。

package com.savor.ads.okhttp.coreProgress.download;

import android.text.TextUtils;
import android.util.Log;

import com.savor.ads.service.MiniProgramNettyService;
import com.savor.ads.utils.ConstantValues;
import com.savor.ads.utils.LogFileUtil;
import com.savor.ads.utils.LogUtils;

import org.json.JSONObject;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Created by Administrator on 2016/12/9.
 * Modify  by bichao on 2019-06-25 13:41
 */

public class ProgressDownloader {
    public static final String TAG = "ProgressDownloader";
    public final static int CONNECT_TIMEOUT =60;
    public final static int READ_TIMEOUT=10;
    public final static int WRITE_TIMEOUT=10;
    private String url;
    private OkHttpClient client;
    //下载文件存储的位置
    private String filePath;
    private String fileName;
    private long fileSize;
    //下载失败重复下载次数,最多3次
    private int downloadCount=0;
    private Call call;
    private MiniProgramNettyService.DownloadProgressListener downloadProgressListener;

    public void setDownloadProgressListener(MiniProgramNettyService.DownloadProgressListener listener){
        downloadProgressListener = listener;
    }

    public ProgressDownloader (String url,String filePath,String fileName){
        this.url = url;
        this.filePath = filePath;
        this.fileName = fileName;
        //在下载、暂停后的继续下载中可复用同一个client对象
        client = getProgressClient();
    }
    public ProgressDownloader (String url,String filePath,String fileName,long resourceSize){
        this.url = url;
        this.filePath = filePath;
        this.fileName = fileName;
        this.fileSize = resourceSize;
        //在下载、暂停后的继续下载中可复用同一个client对象
        client = getProgressClient();
    }
    //每次下载需要新建新的Call对象
    private Call newRangeCall(long startPoints){
        Request request = new Request.Builder()
                .url(url)
                .header("Range","bytes="+startPoints+"-")//断点续传下载需要用到的,提示下载的区域
                .build();
        return client.newCall(request);
    }
    private Call newCall(){
        Request request = new Request.Builder()
                .url(url)
                .build();
        return client.newCall(request);
    }


    public OkHttpClient getProgressClient(){
        //拦截器,用上ProgressResponseBody
        Interceptor interceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response originalResponse = chain.proceed(chain.request());
                return originalResponse.newBuilder()
                        .body(new ProgressResponseBody(originalResponse.body(),fileName))
                        .build();
            }
        };
        return new OkHttpClient.Builder()
                .addNetworkInterceptor(interceptor)
                .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(WRITE_TIMEOUT,TimeUnit.SECONDS)//设置写的超时时间
                .connectTimeout(CONNECT_TIMEOUT,TimeUnit.SECONDS)//设置连接超时时间
                .build();
    }
   //下载方法
    public boolean downloadByRange(){
        long startIndex = 0;
        boolean flag = false;
        try {
            File cacheFile = new File(filePath,fileName+ ConstantValues.CACHE);
            if (cacheFile.exists()){
                RandomAccessFile cacheAccessFile = new RandomAccessFile(cacheFile,"rwd");
                try {
                    startIndex = cacheAccessFile.length();
                }catch (NumberFormatException e){
                        e.printStackTrace();
                }
                call = newRangeCall(startIndex);
                Response response = call.execute();
                if (response.code()==206){
                    flag = saveRangeFile(response,startIndex,cacheFile);
                }
            }else{
                call = newCall();
                Response response = call.execute();
                if (response.code() == 200) {
                    flag = saveRangeFile(response,startIndex,cacheFile);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return flag;
    }

    private boolean saveRangeFile(Response response,long startIndex,File cacheFile) throws IOException{
        LogFileUtil.writeDownloadLog("下载文件--开始--fileName="+fileName+",fileLength="+startIndex);
        boolean flag;
        InputStream is = null;
        RandomAccessFile tmpAccessFile = new RandomAccessFile(cacheFile, "rw");// 获取前面已创建的文
        tmpAccessFile.seek(startIndex);
        try {
            is = response.body().byteStream();// 获取流
            byte[] buffer = new byte[1024*1024*3];
            int length;
            while ((length = is.read(buffer)) > 0) {//读取流
                tmpAccessFile.write(buffer, 0, length);
                if (downloadProgressListener!=null&&fileSize!=0){
                    long cacheLength = tmpAccessFile.length();
                    countDownlaodProgess(cacheLength,fileSize);
                }
            }
            flag = true;
        }catch (Exception e){
            flag = false;
            e.printStackTrace();
        }finally {
            if (is!=null){
                is.close();
            }
            close(response);
        }
        if (flag){
            cacheFile.renameTo(new File(filePath+fileName));
            LogFileUtil.writeDownloadLog("下载文件--完成--fileName="+fileName+",fileLength="+tmpAccessFile.length());
        }else{
            if (downloadCount<3){
                downloadByRange();
            }
            downloadCount ++;
        }
        return flag;
    }
    //计算出一个百分比的字符串
    private void countDownlaodProgess(long currentSize,long totalSize) {
        BigDecimal b = new BigDecimal(currentSize * 1.0 / totalSize);
//        Log.d("circularProgress", "原始除法得到的值" + currentSize * 1.0 / totalSize);
        float f1 = b.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
//        Log.d("circularProgress", "保留两位小数得到的值" + f1);
        if (f1 >= 0.01f) {
            String value = String.valueOf(f1 * 100);
            int progress = Integer.valueOf(value.split("\\.")[0]);
            downloadProgressListener.getDownloadProgress(progress+"%");
            Log.d("downloadProgress", "保留两位小数得到的值" + f1);
        }
    }
    /**
     * 关闭资源
     *
     * @param closeables
     */
    private void close(Closeable... closeables) {
        int length = closeables.length;
        try {
            for (int i = 0; i < length; i++) {
                Closeable closeable = closeables[i];
                if (null != closeable){
                    closeables[i].close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            for (int i = 0; i < length; i++) {
                closeables[i] = null;
            }
        }
    }
}



package com.savor.ads.okhttp.coreProgress.download;

import android.util.Log;

import com.savor.ads.utils.LogUtils;

import java.io.IOException;


import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;

/**
 * 包装的响体,处理进度
 * Created by Administrator on 2016/6/14.
 */
public class ProgressResponseBody extends ResponseBody {
    private final ResponseBody responseBody;
    private String fileName;
    private BufferedSource bufferedSource;

    public ProgressResponseBody(ResponseBody responseBody, String filename) {
        this.responseBody = responseBody;
        this.fileName = filename;
    }

    public ProgressResponseBody(ResponseBody responseBody) {
        this.responseBody = responseBody;
    }

    @Override
    public MediaType contentType() {
        return responseBody.contentType();
    }


    @Override
    public long contentLength() {
        return responseBody.contentLength();
    }

    @Override
    public BufferedSource source() {
        if (bufferedSource == null) {
            bufferedSource = Okio.buffer(source(responseBody.source()));
        }
        return bufferedSource;
    }

    private Source source(Source source) {
        return new ForwardingSource(source) {
            long totalBytesRead = 0L;

            @Override
            public long read(Buffer sink, long byteCount) throws IOException {
                long bytesRead = super.read(sink, byteCount);
                totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                LogUtils.v("fileName="+fileName+":read-----" + totalBytesRead);
                return bytesRead;
            }
        };
    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值