前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Retrofit2.3使用姊妹篇——带进度上传文件

Retrofit2.3使用姊妹篇——带进度上传文件

作者头像
蜻蜓队长
发布2018-08-03 11:19:11
2.4K0
发布2018-08-03 11:19:11
举报
文章被收录于专栏:Android机动车Android机动车

之前的一篇博客讲了Retrofit实现带进度下载的实现,算是Retrofit使用的“姐姐篇”,那今天我们就讲讲它的“妹妹篇“——用Retrofit实现带进度上传文件!

github地址:https://github.com/kb18519142009/UploadService.git 大家喜欢的话,就给个star^_^,有问题或者建议,可以直接提issues,也可以在博客下面给我留言。谢谢~

还是先上效果图:

上传图片效果

上传视频效果

这里我分别实现了图片和视频的上传,并附带有进度显示,为了更直观的展示上传效果,我写了图片选择和视频选择两个列表,将手机本地相册内的图片和视频全部展示出来(读取图片和视频的方法可以看这篇博客),有兴趣的可以直接去github下载demo查看,这里就不多说了,好了,接下来我们步入正题吧!

一、添加依赖

代码语言:javascript
复制
    implementation 'com.android.support:recyclerview-v7:26.1.0' //recyclerview
    implementation 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit2
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0' //gson解析
    implementation 'com.github.bumptech.glide:glide:4.3.1' //glide加载图片
    implementation 'com.jakewharton:butterknife:8.8.1' //黄油刀
    implementation 'com.github.castorflex.smoothprogressbar:library-circular:1.3.0' //环形进度条
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' //黄油刀注解

二、添加权限和动态权限处理

在清单文件AndroidManifest中的manifest节点中添加以下代码:

代码语言:javascript
复制
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

要实现将文件上传,我们需要网络权限和内存的读写权限,由于我在图片选择列表里加了拍照功能,所以这里加上了相机的权限。 注意:由于我们用到了写入内存和相机的权限,所以千万要注意6.0以上动态权限的申请!demo里依然用的是自己简单封装的权限申请工具类,大家可以直接去看demo里的使用!

三、设计回调

代码语言:javascript
复制
public interface UploadCallbacks {
        void onProgressUpdate(int percentage);
        void onError();
        void onFinish();
    }

回调中包括上传进度、错误回调和结束回调等四个方法。其中我们在上传进度的回调中返回进度的百分比,在此可以将进度显示在控件上。如果你还有一些个性化的需求,可以自行添加。

四、网络工具类准备

对Retrofit进行简单封装。

代码语言:javascript
复制
/**
 * ApiHelper 网络请求工具类
 * Created by kang on 2018/3/9.
 */
public class ApiHelper {
    private static final String TAG = "ApiHelper";
    public static final String BASE_URL = "http://192.168.1.200:9090/";
    private static ApiHelper mInstance;
    private Retrofit mRetrofit;
    private OkHttpClient mHttpClient;
    private ApiHelper() {
        this(30, 30, 30);
    }
    public ApiHelper(int connTimeout, int readTimeout, int writeTimeout) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(connTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS);
        mHttpClient = builder.build();
    }
    public static ApiHelper getInstance() {
        if (mInstance == null) {
            mInstance = new ApiHelper();
        }
        return mInstance;
    }
    public ApiHelper buildRetrofit(String baseUrl) {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(mHttpClient)
                .addConverterFactory(GsonConverterFactory.create(new Gson()))
                .build();
        return this;
    }
    public <T> T createService(Class<T> serviceClass) {
        return mRetrofit.create(serviceClass);
    }
}

上传接口,注意@Multipart注解!

代码语言:javascript
复制
/**
 * Description:网络请求接口类
 * Created by kang on 2018/3/9.
 */
public interface ApiInterface {
    /**
     * 文件整块上传
     *
     * @param file
     * @return
     */
    @Multipart
    @POST("v1/upload")
    Call<UploadVideoResp> uploadFile(@Part MultipartBody.Part file);
}

五、继承RequestBody 类

代码语言:javascript
复制
public class ProgressRequestBody extends RequestBody {
    private File mFile;
    private String mPath;
    private String mMediaType;
    private UploadCallbacks mListener;
    private int mEachBufferSize = 1024;
    public ProgressRequestBody(final File file, String mediaType, final UploadCallbacks listener) {
        mFile = file;
        mMediaType = mediaType;
        mListener = listener;
    }
    public ProgressRequestBody(final File file, String mediaType, int eachBufferSize, final UploadCallbacks listener) {
        mFile = file;
        mMediaType = mediaType;
        mEachBufferSize = eachBufferSize;
        mListener = listener;
    }
    @Override
    public MediaType contentType() {
        // i want to upload only images
        return MediaType.parse(mMediaType);
    }
    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        long fileLength = mFile.length();
        byte[] buffer = new byte[mEachBufferSize];
        FileInputStream in = new FileInputStream(mFile);
        long uploaded = 0;
        try {
            int read;
            Handler handler = new Handler(Looper.getMainLooper());
            while ((read = in.read(buffer)) != -1) {
                // update progress on UI thread
                handler.post(new ProgressUpdater(uploaded, fileLength));
                uploaded += read;
                sink.write(buffer, 0, read);
            }
        } finally {
            in.close();
        }
    }
    private class ProgressUpdater implements Runnable {
        private long mUploaded;
        private long mTotal;
        public ProgressUpdater(long uploaded, long total) {
            mUploaded = uploaded;
            mTotal = total;
        }
        @Override
        public void run() {
            mListener.onProgressUpdate((int) (100 * mUploaded / mTotal));
        }
    }
}

Retrofit虽然没有直接为我们提供上传进度的接口,但是它提供了RequestBody 类,我们通过继承RequestBody类,重写writeTo方法即可获取上传进度! 1、首先我们还是看一下ProgressRequestBody 这个类的构造函数,这里我提供了两个构造:

  • 1、传入要上传的文件对象file、文件类型mediaType和上传回调。
代码语言:javascript
复制
 public ProgressRequestBody(final File file, String mediaType, final UploadCallbacks listener) {
        mFile = file;
        mMediaType = mediaType;
        mListener = listener;
    }
  • 2、传入要上传的文件对象file、文件类型mediaType、上传buffer大小和上传回调。
代码语言:javascript
复制
public ProgressRequestBody(final File file, String mediaType, int eachBufferSize, final UploadCallbacks listener) {
        mFile = file;
        mMediaType = mediaType;
        mEachBufferSize = eachBufferSize;
        mListener = listener;
    }

其中mediaType可以是“image/*”、“video/mp4”等文件类型(了解更多类型),eachBufferSize是将来上传时Buffer的大小。

2、接下来在重写的contentType()方法中返回文件类型mMediaType。

代码语言:javascript
复制
@Override
    public MediaType contentType() {
        // i want to upload only images
        return MediaType.parse(mMediaType);
    }

3、准备一个Runnable,在构造中传入当前已上传的文件大小uploaded和文件总长度total,然后在 run()方法中通过之前设计好的回调onProgressUpdate将进度传出。

代码语言:javascript
复制
private class ProgressUpdater implements Runnable {
        private long mUploaded;
        private long mTotal;
        public ProgressUpdater(long uploaded, long total) {
            mUploaded = uploaded;
            mTotal = total;
        }
        @Override
        public void run() {
            mListener.onProgressUpdate((int) (100 * mUploaded / mTotal));
        }
    }

4、重写writeTo方法

代码语言:javascript
复制
@Override
    public void writeTo(BufferedSink sink) throws IOException {
        long fileLength = mFile.length();
        byte[] buffer = new byte[mEachBufferSize];
        FileInputStream in = new FileInputStream(mFile);
        long uploaded = 0;
        try {
            int read;
            Handler handler = new Handler(Looper.getMainLooper());
            while ((read = in.read(buffer)) != -1) {
                // update progress on UI thread
                handler.post(new ProgressUpdater(uploaded, fileLength));
                uploaded += read;
                sink.write(buffer, 0, read);
            }
        } finally {
            in.close();
        }
    }
  • 1、在writeTo中我们拿到文件的总长度,输入流,创建byte数组;
  • 2、创建Handler对象,注意创建时传入Looper.getMainLooper()主线程的Looper对象,这样就可以将线程切换到主线程,也就是说在进度回调中便可以直接将进度显示到控件上啦;
  • 3、循环将输入流写入buffer,然后调用handler.post(new ProgressUpdater(uploaded,fileLength)),将当前已上传的长度和总长度传出;
  • 4、将每次循环写入的长度累加到uploaded 上;
  • 5、通过BufferedSink对象的write方法将buffer里的内容写入缓存,这是上传最重要的一步!
  • 6、别忘记在finally中关闭输入流。

六、具体使用

代码语言:javascript
复制
private void uploadPicture() {
        mFlCircleProgress.setVisibility(View.VISIBLE);
        File file = new File(mPicPath);
        //是否需要压缩
        //实现上传进度监听
        ProgressRequestBody requestFile = new ProgressRequestBody(file, "image/*", new ProgressRequestBody.UploadCallbacks() {
            @Override
            public void onProgressUpdate(int percentage) {
                Log.e(TAG, "onProgressUpdate: " + percentage);
                mCircleProgress.setProgress(percentage);
            }
            @Override
            public void onError() {
            }
            @Override
            public void onFinish() {
            }
        });
        MultipartBody.Part body =
                MultipartBody.Part.createFormData("file", file.getName(), requestFile);
        mApi.uploadFile(body).enqueue(new Callback<UploadVideoResp>() {
            @Override
            public void onResponse(Call<UploadVideoResp> call, Response<UploadVideoResp> response) {
                mFlCircleProgress.setVisibility(View.GONE);
                UploadVideoResp resp = response.body();
                if (resp != null) {
                    Toast.makeText(mContext, "图片上传成功!", Toast.LENGTH_SHORT).show();
                }
            }
            @Override
            public void onFailure(Call<UploadVideoResp> call, Throwable t) {
                mFlCircleProgress.setVisibility(View.GONE);
                Toast.makeText(mContext, "图片上传失败,稍后重试", Toast.LENGTH_SHORT).show();
            }
        });
    }

1、先创建一个ProgressRequestBody对象requestFile; 2、通过MultipartBody.Part.createFormData("file", file.getName(), requestFile)方法创建一个MultipartBody.Part对象; 3、调用网络请求接口,出入MultipartBody.Part对象进行上传! 4、在onProgressUpdate回调中显示进度!

OK!大功告成!

demo地址github:https://github.com/kb18519142009/UploadService.git 大家喜欢的话,就给个star^_^,有问题或者建议,可以直接提issues,也可以在博客下面给我留言。谢谢~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android机动车 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、添加依赖
  • 二、添加权限和动态权限处理
  • 三、设计回调
  • 四、网络工具类准备
  • 五、继承RequestBody 类
  • 六、具体使用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档