Android 开发 框架系列 OkHttp文件下载功能实现(含断点续传)

前言

  此篇博客只是下载功能的记录demo,如果你还不太了解okhttp可以参考我的另一篇博客 https://www.cnblogs.com/guanxinjing/p/9708575.html

代码部分

package okhttpdemo.com.libs.net.httpBase;
import android.util.Log;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttpdemo.com.libs.net.httpBase.listener.HttpDownListener;
/**
 *@content: okhttp的下载功能工具类 (分别包含:1.无断点续传的get下载 2.有断点续传的get下载 3.无断点续传的post下载 4.有断点续传的post下载)
 *@time:2018-12-12
 *@build:z
 */

public class OkHttpDownUtil {
    private static final String TAG = "OkHttpDownUtil";
    private Call mCall;
    private long mAlreadyDownLength = 0;//已经下载长度
    private long mTotalLength = 0;//整体文件大小
    private int mSign = 0; //标记当前运行的是那个方法
    private String mDownUrl;//下载网络地址
    private File mPath;//文件保存路径
    private JSONObject mJson;
    private HttpDownListener mHttpDownListener;//下载进度接口回调


    /**
     * 没有断点续传功能的get请求下载
     * @param downUrl 下载网络地址
     * @param saveFilePathAndName 保存路径
     */
    public void getDownRequest(final String downUrl, final File saveFilePathAndName, final HttpDownListener listener) {
        synchronized (this) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mSign = 1;
                    mDownUrl = downUrl;
                    mPath = saveFilePathAndName;
                    mHttpDownListener = listener;
                    mAlreadyDownLength = 0;
                    Request request = new Request.Builder()
                            .url(mDownUrl)
                            .get()
                            .build();
                    mCall = OkHttpClientCreate.CreateClient().newCall(request);//构建了一个完整的http请求
                    mCall.enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
                            if (mHttpDownListener != null) {
                                mHttpDownListener.onFailure(call, e);
                            }

                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            ResponseBody responseBody = response.body();
                            mTotalLength = responseBody.contentLength();//下载文件的总长度
                            InputStream inp = responseBody.byteStream();
                            FileOutputStream fileOutputStream = new FileOutputStream(mPath);
                            try {
                                byte[] bytes = new byte[2048];
                                int len = 0;
                                while ((len = inp.read(bytes)) != -1) {
                                    mAlreadyDownLength = mAlreadyDownLength + len;
                                    fileOutputStream.write(bytes, 0, len);
                                    if (mHttpDownListener != null) {
                                        mHttpDownListener.onResponse(call, response, mTotalLength, mAlreadyDownLength);
                                    }

                                }
                            } catch (Exception e) {
                                Log.e(TAG, "Get下载异常");

                            } finally {
                                fileOutputStream.close();
                                inp.close();
                                Log.e(TAG, "流关闭");
                            }
                        }
                    });


                }
            }).start();
        }
    }

    /**
     * 有断点续传功能的get下载
     * @param downUrl 下载地址
     * @param saveFilePathAndName 保存路径
     * @param listener 进度监听
     */
    public void getRenewalDownRequest(final String downUrl, final File saveFilePathAndName, final HttpDownListener listener){
        synchronized (this) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mSign = 2;
                    mDownUrl = downUrl;
                    mPath = saveFilePathAndName;
                    mHttpDownListener = listener;
                    Request request = new Request.Builder()
                            .url(mDownUrl)
                            .header("RANGE", "bytes=" + mAlreadyDownLength + "-")
                            .build();
                    mCall = OkHttpClientCreate.CreateClient().newCall(request);//构建了一个完整的http请求
                    mCall.enqueue(new Callback() { //发送请求
                        @Override
                        public void onFailure(Call call, IOException e) {
                            if (mHttpDownListener != null) {
                                mHttpDownListener.onFailure(call, e);
                            }
                            Log.e(TAG, "onFailure: 异常报错=" + e.toString());

                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            ResponseBody responseBody = response.body();
                            InputStream inputStream = responseBody.byteStream();//得到输入流
                            RandomAccessFile randomAccessFile = new RandomAccessFile(mPath, "rw");//得到任意保存文件处理类实例
                            if (mTotalLength == 0){
                                mTotalLength = responseBody.contentLength();//得到文件的总字节大小
                                randomAccessFile.setLength(mTotalLength);//预设创建一个总字节的占位文件
                            }
                            if (mAlreadyDownLength != 0){
                                randomAccessFile.seek(mAlreadyDownLength);
                            }
                            byte[] bytes = new byte[2048];
                            int len = 0;
                            try {
                                while ((len = inputStream.read(bytes)) != -1) {
                                    randomAccessFile.write(bytes,0,len);
                                    mAlreadyDownLength = mAlreadyDownLength + len;
                                    if (mHttpDownListener != null) {
                                        mHttpDownListener.onResponse(call, response, mTotalLength, mAlreadyDownLength);
                                    }
                                }

                            } catch (Exception e) {
                                Log.e(TAG, "Get下载异常");

                            } finally {
                                mAlreadyDownLength = randomAccessFile.getFilePointer();//记录当前保存文件的位置
                                randomAccessFile.close();
                                inputStream.close();
                                Log.e(TAG, "流关闭 下载的位置="+mAlreadyDownLength);
                            }

                        }
                    });

                }
            }).start();
        }

    }

    /**
     * 没有断点续传的post下载
     * @param downUrl
     * @param saveFilePathAndName
     * @param json
     * @param listener
     */
    public void postDownRequest(final String downUrl, final File saveFilePathAndName, final JSONObject json,final HttpDownListener listener){
       synchronized (this){
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mSign = 3;
                   mDownUrl = downUrl;
                   mPath = saveFilePathAndName;
                   mJson = json;
                   mHttpDownListener = listener;
                   mAlreadyDownLength = 0;
                   Request request = new Request.Builder()
                           .url(mDownUrl)
                           .post(changeJSON(json))
                           .build();
                   mCall = OkHttpClientCreate.CreateClient().newCall(request);
                   mCall.enqueue(new Callback() {
                       @Override
                       public void onFailure(Call call, IOException e) {
                           if (mHttpDownListener!=null){
                               mHttpDownListener.onFailure(call,e);
                           }
                       }

                       @Override
                       public void onResponse(Call call, Response response) throws IOException {

                           ResponseBody responseBody = response.body();
                           mTotalLength = responseBody.contentLength();
                           InputStream inputStream = responseBody.byteStream();
                           FileOutputStream fileOutputStream = new FileOutputStream(mPath);
                           byte[] bytes = new byte[2048];
                           int len = 0;
                           try {
                               while ((len = inputStream.read(bytes)) != -1) {
                                   fileOutputStream.write(bytes, 0, len);
                                   mAlreadyDownLength = mAlreadyDownLength + len;
                                   if (mHttpDownListener != null) {
                                       mHttpDownListener.onResponse(call, response, mTotalLength, mAlreadyDownLength);
                                   }
                               }
                           }catch (Exception e){
                               Log.e(TAG, "Post下载异常");
                           }finally {
                               fileOutputStream.close();
                               inputStream.close();
                               Log.e(TAG, "流关闭");

                           }

                       }

                   });
               }
           }).start();
       }


    }

    /**
     * 支持断点续传的post下载
     * @param downUrl 下载网址
     * @param saveFilePathAndName 文件保存路径
     * @param json 参数
     * @param listener 接口回调
     */
    public void postRenewalDownRequest(final String downUrl, final File saveFilePathAndName, final JSONObject json, final HttpDownListener listener){
        synchronized (this){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mSign = 4;
                    mDownUrl = downUrl;
                    mPath = saveFilePathAndName;
                    mJson = json;
                    mHttpDownListener = listener;
                    Request request = new Request.Builder()
                            .url(mDownUrl)
                            .header("RANGE","bytes="+mAlreadyDownLength+"-")
                            .post(changeJSON(json))
                            .build();
                    mCall = OkHttpClientCreate.CreateClient().newCall(request);
                    mCall.enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
                            if (mHttpDownListener != null){
                                mHttpDownListener.onFailure(call,e);
                            }
                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            ResponseBody responseBody = response.body();
                            InputStream inputStream = responseBody.byteStream();
                            RandomAccessFile randomAccessFile = new RandomAccessFile(mPath,"rw");
                            if (mTotalLength == 0){
                                mTotalLength = responseBody.contentLength();
                                randomAccessFile.setLength(mTotalLength);
                            }
                            if (mAlreadyDownLength!=0){
                                randomAccessFile.seek(mAlreadyDownLength);
                            }
                            byte[] bytes = new byte[2048];
                            int len = 0;
                            try {
                                while ((len = inputStream.read(bytes)) != -1) {
                                    randomAccessFile.write(bytes, 0, len);
                                    mAlreadyDownLength = mAlreadyDownLength + len;
                                    if (mHttpDownListener != null) {
                                        mHttpDownListener.onResponse(call, response, mTotalLength, mAlreadyDownLength);
                                    }

                                }
                            }catch (Exception e){
                                Log.e(TAG, "Post下载异常");

                            }finally {
                                mAlreadyDownLength = randomAccessFile.getFilePointer();
                                randomAccessFile.close();
                                inputStream.close();
                                Log.e(TAG, "流关闭 下载的位置="+mAlreadyDownLength);
                            }

                        }
                    });

                }
            }).start();
        }

    }

    /**
     * 恢复下载
     */
    public void resume(){
        if (mSign==0){
            return;
        }
        switch (mSign){
            case 1:
                getDownRequest(mDownUrl,mPath,mHttpDownListener);
                break;
            case 2:
                getRenewalDownRequest(mDownUrl,mPath,mHttpDownListener);
                break;
            case 3:
                postDownRequest(mDownUrl,mPath,mJson,mHttpDownListener);
                break;
            case 4:
                postRenewalDownRequest(mDownUrl,mPath,mJson,mHttpDownListener);
                break;
            default:
                break;
        }

    }

    /**
     * 暂停下载
     */
    public void stop(){
        if (mCall!=null){
            mCall.cancel();
        }

    }

    /**
     * 删除下载文件
     */
    public void deleteCurrentFile(){
        if (mPath == null){
            Log.e(TAG, "deleteCurrentFile error : 没有路径");
            return;
        }
        if (!mPath.exists()){
            Log.e(TAG, "deleteCurrentFile error: 文件不存在");
            return;
        }
        mPath.delete();
        mAlreadyDownLength = 0;
        mTotalLength = 0;
        mSign = 0;
    }

    /**
     * 销毁
     */
    public void destroy(){
        if (mCall!=null){
            mCall.cancel();
            mCall = null;
        }
        mSign = 0;
        mDownUrl = null;
        mPath = null;
        mHttpDownListener = null;
        mAlreadyDownLength = 0;
        mTotalLength = 0;
    }

    /**
     * 转换Json参数为RequestBody
     * @param jsonParam json对象
     * @return RequestBody
     */
    private RequestBody changeJSON(JSONObject jsonParam){
        RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8")
                , String.valueOf(jsonParam));
        return requestBody;
    }

}

 

上一篇:genymotion解决下载设备慢的问题


下一篇:android – Retrofit响应缓存