OkHttp文件上传和自定义缓存

一,文件上传

    1. MainActivity中的应用

package com.darren.architect_day27;

import android.Manifest;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;

import com.tbruyelle.rxpermissions2.RxPermissions;

import java.io.File;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.URLConnection;

import io.reactivex.functions.Consumer;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // rxPermission
        RxPermissions rxPermissions = new RxPermissions(this);

        rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE)
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        if(aBoolean){
                            // 权限申请,并且用户给了权限
                            uploadFile();
                        }
                    }
                });
    }

    private void uploadFile() {
        // 这个是 Okhttp 上传文件的用法
        String url = "https://api.saiwuquan.com/api/upload";
        File file = new File(Environment.getExternalStorageDirectory(), "test.apk");
        OkHttpClient httpClient = new OkHttpClient();
        // 构建请求 Body , 这个我们之前自己动手写过
        MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM);
        builder.addFormDataPart("platform", "android");
        builder.addFormDataPart("file", file.getName(),
                RequestBody.create(MediaType.parse(guessMimeType(file.getAbsolutePath())), file));

        ExMultipartBody exMultipartBody = new ExMultipartBody(builder.build()
                ,new UploadProgressListener(){

            @Override
            public void onProgress(long total, long current) {
                showToast(total,current);
            }
        });

        // 怎么监听上传文件的进度?

        // 构建一个请求
        final Request request = new Request.Builder()
                .url(url)
                .post(exMultipartBody).build();
        // new RealCall 发起请求
        Call call = httpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("TAG", response.body().string());
            }
        });
    }

    private void showToast(final long total, final long current) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //Toast.makeText(MainActivity.this,current+"/"+total,Toast.LENGTH_LONG).show();
            }
        });
    }

    private String guessMimeType(String filePath) {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();

        String mimType = fileNameMap.getContentTypeFor(filePath);

        if(TextUtils.isEmpty(mimType)){
            return "application/octet-stream";
        }
        return mimType;
    }
}

2. 静态代理

package com.darren.architect_day27;

import android.support.annotation.Nullable;
import android.util.Log;

import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;

/**
 * 静态代理设计模式  implements Target
 * Created by hcDarren on 2017/11/25.
 */
public class ExMultipartBody extends RequestBody {
    private RequestBody mRequestBody;
    private int mCurrentLength;
    private UploadProgressListener mProgressListener;

    public ExMultipartBody(MultipartBody requestBody) {
        this.mRequestBody = requestBody;
    }

    public ExMultipartBody(MultipartBody requestBody, UploadProgressListener progressListener) {
        this.mRequestBody = requestBody;
        this.mProgressListener = progressListener;
    }

    @Nullable
    @Override
    public MediaType contentType() {
        // 静态代理最终还是调用的代理对象的方法
        return mRequestBody.contentType();
    }

    @Override
    public long contentLength() throws IOException {
        return mRequestBody.contentLength();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        Log.e("TAG","监听");
        // 总的长度
        final long contentLength = contentLength();
        // 获取当前写了多少数据?BufferedSink Sink(okio 就是 io )就是一个 服务器的 输出流,我还是不知道写了多少数据

        // 又来一个代理 ForwardingSink
        ForwardingSink forwardingSink = new ForwardingSink(sink) {
            @Override
            public void write(Buffer source, long byteCount) throws IOException {
                // 每次写都会来这里
                mCurrentLength += byteCount;
                if(mProgressListener!=null){
                    mProgressListener.onProgress(contentLength,mCurrentLength);
                }
                Log.e("TAG",contentLength+" : "+mCurrentLength);
                super.write(source, byteCount);
            }
        };
        // 转一把
        BufferedSink bufferedSink = Okio.buffer(forwardingSink);
        mRequestBody.writeTo(bufferedSink);
        // 刷新,RealConnection 连接池
        bufferedSink.flush();
    }
}

3.回调接口

package com.darren.architect_day27;

/**
 * Created by hcDarren on 2017/11/25.
 */
public interface UploadProgressListener {
    void onProgress(long total,long current);
}

二,OkHttp自定义缓存拦截

1. MainActivity中的应用

package com.darren.architect_day27.simple2;

import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import com.darren.architect_day27.R;

import java.io.File;
import java.io.IOException;

import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {
    private OkHttpClient mHttpClient;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 自定义缓存(要求:有网 30s 内请求读缓存,无网直接读缓存)
        // OkHttp 自带的扩展有坑,我们之前自己写过这个缓存管理,与 OkHttp 结合就可以了

        // 思路?拦截器?分为两种
        File file = new File(Environment.getExternalStorageDirectory(),"cache");
        Cache cache = new Cache(file,100*1024*1024);
        mHttpClient = new OkHttpClient.Builder()
                .cache(cache)
                // 加载最前 过期时间缓存多少秒  没网读缓存
                .addInterceptor(new CacheRequestInterceptor(this))
                // 加载最后,数据缓存 过期时间 30s  对Response的操作
                .addNetworkInterceptor(new CacheResponseInterceptor())
                .build();
    }

    public void click(View view){
        String url = "https://api.saiwuquan.com/api/appv2/sceneModel";
        // 构建一个请求
        final Request request = new Request.Builder()
                .url(url).build();
        // new RealCall 发起请求
        Call call = mHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("TAG",response.body().string());
                // 都是有 第一把,第二把没有网络的了只有缓存的 (30s 以内),过了 30s 之后又会有网络的了(会再请求更新)
                Log.e("TAG", response.cacheResponse()+" ; "+response.networkResponse());
            }
        });
    }
}

2. 放在ConnectInterceptor 和 CallServerInterceptor 之间的自定义拦截器,设置缓存保持为3.秒

package com.darren.architect_day27.simple2;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Response;

/**
 * Created by hcDarren on 2017/11/25.
 */

public class CacheResponseInterceptor implements Interceptor{
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        // 过期时间是 30s
        response = response.newBuilder()
                .removeHeader("Cache-Control")
                .removeHeader("Pragma")
                .addHeader("Cache-Control","max-age="+30).build();
        return response;
    }
}

3. 放在retryAndFollowUpInterceptor 之前 自定义缓存拦截器,设定为有网就30秒缓存,过期就重新请求网络,没有过期就读取缓存。如果没有网络,就直接读取缓存。

package com.darren.architect_day27.simple2;

import android.content.Context;

import java.io.IOException;

import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by hcDarren on 2017/11/25.
 */

public class CacheRequestInterceptor implements Interceptor{
    private Context mContext;
    public CacheRequestInterceptor(Context context){
        this.mContext = context;
    }
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if(isNetWork(mContext)){
            // 只读缓存
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE).build();
        }
        return chain.proceed(request);
    }

    private boolean isNetWork(Context mContext) {
        // 自己去完善
        return true;
    }
}

猜你喜欢

转载自blog.csdn.net/lyfxh1314/article/details/86549367