结合 第一篇文章对Retrofit的封装,本篇文章将讲述如何实现文件上传与下载。本篇文章可分为文件上传与文件下载两部分内容。

一、使用RxJava+Retrofit上传文件

使用Retrofit上传文件到服务器可分为单文件上传与多文件上传,实现都比较简单。不妨用两个例子来分别看下单文件和多文件上传。

1 、实现单文件上传
单文件上传最常见的就是更换头像,我们就以此为例来看。
首先定义上传头像的接口方法,代码如下:

    @Multipart@POST("user/uploadAvatar.do")Observable<UploadAvatarResponse> uploadAvatar(@Part("userId") RequestBody userId,@Part MultipartBody.Part image);

注意上面上面方法加了@Multipart的注解。对于上传文件必须要加这个注解,不然会报异常!另外方法中有两个参数,即UserId和要上传的头像文件!返回值是我我们自定义的Observable(详见上一片文章),接下来在我们注册的页面调用这个方法,如下:

 File file = new File(picPath);//  图片参数
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);MultipartBody.Part imageBody = MultipartBody.Part.createFormData("uploadFile", file.getName(), requestFile);//  手机号参数
RequestBody userIdBody = RequestBody.create(MediaType.parse("multipart/form-data"), phone);IdeaApi.getApiService().uploadAvatar(userIdBody,imageBody ).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DefaultObserver<UploadAvatarResponse>(this, true) {@Overridepublic void onSuccess(UploadAvatarResponseresponse) {EventBus.getDefault().post(new RegisterSuccess("register success"));showToast("头像上传成功");finish();}});

显然,上面的方法有个弊端。当接口中需要的参数较少时使用上面的方法无可厚非。如果接口中需要的参数非常多,那么上面的方法使用起来就麻烦了。因此对于参数较多的单文件上传可以使将所有参数都放入一个List集合中。同样以上传头像为例。

先定义上传头像接口的方法:

    @Multipart@POST("user/register.do")Observable<UploadAvatarResponse> register(@Part List<MultipartBody.Part> partList);

可以看到现在方法中间参数变为一个List《ltipartBody.Part》的集合。这样所有的参数我们只需要放到这个集合里边即可!接下来看注册页面如何调用这个方法:

File file = new File(picPath);RequestBody imageBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("userId", userId).addFormDataPart("uploadFile", file.getName(), imageBody);List<MultipartBody.Part> parts = builder.build().parts();IdeaApi.getApiService().register(parts).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DefaultObserver<UploadAvatarResponse>(this, true) {@Overridepublic void onSuccess(UploadAvatarResponse response) {EventBus.getDefault().post(new RegisterSuccess("register success"));showToast("注册成功,请登陆");finish();}

这样是不是比第一种方法清爽了很多呢!

2.实现多文件上传。
对于多图上传其实跟单文件上传没有多大区别,只不过多了些参数而已。先看定义多文件上传接口:

 @POST("upload/uploadPic")Observable<UpLoadMultiFileResponse> uploadFiles(@Part("filename") String description,@Part("pic\"; filename=\"image1.png") RequestBody imgs1,@Part("pic\"; filename=\"image2.png") RequestBody imgs2,);

调用接口上传图片:

    File file = new File(picPath);RequestBody requestFile1 = RequestBody.create(MediaType.parse("multipart/form-data"), file);MultipartBody.Part body = MultipartBody.Part.createFormData("uploadFile", file.getName(), requestFile);RequestBody requestFile2 = RequestBody.create(MediaType.parse("multipart/form-data"), file);MultipartBody.Part body = MultipartBody.Part.createFormData("uploadFile", file.getName(), requestFile);IdeaApi.getApiService().uploadFiles("pictures",requestFile1,requestFile2 ).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DefaultObserver<UpLoadMultiFileResponse>(this, true) {@Overridepublic void onSuccess(UpLoadMultiFileResponse response) {EventBus.getDefault().post(new RegisterSuccess("register success"));showToast("注册成功,请登陆");finish();}

同样,当上传图片较多时可以采用map集合来存放多个图片RequestBody参数。接口代码如下:

  @POST()Observable<BasicResponse> uploadFiles(@Part("filename") String description,@PartMap() Map<String, RequestBody> maps);

然后调用接口实现多文件上传

 File file = new File(picPath);RequestBody requestFile1 = RequestBody.create(MediaType.parse("multipart/form-data"), file);MultipartBody.Part body = MultipartBody.Part.createFormData("uploadFile", file.getName(), requestFile);RequestBody requestFile2 = RequestBody.create(MediaType.parse("multipart/form-data"), file);MultipartBody.Part body = MultipartBody.Part.createFormData("uploadFile", file.getName(), requestFile);Map<String,RequestBody> map=new HashMap<>();map.put("picture1",requestFile1 );map.put("picture2",requestFile2 );IdeaApi.getApiService().uploadFiles("pictures",map).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DefaultObserver<BasicResponse<RegisterBean>>(this, true) {@Overridepublic void onSuccess(BasicResponse<RegisterBean> response) {EventBus.getDefault().post(new RegisterSuccess("register success"));showToast("注册成功,请登陆");finish();}

关于文件上传就这么多东西,实现起来也相当简单。那么接下来第二部分使用Retrofit实现文件下载才是比较重要的内容。

二、使用RxJava+Retrofit下载

使用Retrofit下载文件其实非常简单,但是对于用户来说往往都希望可以看到下载进度,然而遗憾的是Retrofit并没有为我们提供文件下载进度的接口。因此,关于下载进度就需要我们自己来通过拦截器实现。本文不讲解Retrofit下载的基本使用,而是针对Retrofit加入下载进度实现一个简单的封装。老规矩,先来看一下封装后的使用。代码如下:

DownloadUtils downloadUtils = new DownloadUtils();
//  开始下载
public void download(View view) {btn.setClickable(false);downloadUtils.download(Constants.DOWNLOAD_URL, new DownloadListener() {@Overridepublic void onProgress(int progress) {LogUtils.e("--------下载进度:" + progress);Log.e("onProgress", "是否在主线程中运行:" + String.valueOf(Looper.getMainLooper() == Looper.myLooper()));progressBar.setProgress(progress);mTvPercent.setText(String.valueOf(progress) + "%");}@Overridepublic void onSuccess(ResponseBody responseBody) {  //  运行在子线程saveFile(responseBody);Log.e("onSuccess", "是否在主线程中运行:" + String.valueOf(Looper.getMainLooper() == Looper.myLooper()));}@Overridepublic void onFail(String message) {btn.setClickable(true);ToastUtils.show("文件下载失败,失败原因:" + message);Log.e("onFail", "是否在主线程中运行:" + String.valueOf(Looper.getMainLooper() == Looper.myLooper()));}@Overridepublic void onComplete() {  //  运行在主线程中ToastUtils.show("文件下载成功");btn.setClickable(true);}});}
//  取消下载
public void cancelDownload(View view) {if (downloadUtils != null) {downloadUtils.cancelDownload();}}

上面代码中使用DownloadUtils类来实现下载文件与取消下载。并且在下载时回调出了下载进度。那么关于以上代码是如何实现的呢?接下来看详细解析。
**1.获取下载进度。**新建一个ProgressHelper类,在该类中为Retrofit添加拦截器,核心代码如下:

public static OkHttpClient.Builder addProgress(OkHttpClient.Builder builder){if (builder == null){builder = new OkHttpClient.Builder();}final ProgressListener progressListener = new ProgressListener() {//该方法在子线程中运行@Overridepublic void onProgress(long progress, long total, boolean done) {Log.d("progress:",String.format("%d%% done\n",(100 * progress) / total));if (mProgressHandler == null){return;}progressBean.setBytesRead(progress);progressBean.setContentLength(total);progressBean.setDone(done);mProgressHandler.sendMessage(progressBean);}};//添加拦截器,自定义ResponseBody,添加下载进度builder.networkInterceptors().add(new Interceptor() {@Overridepublic okhttp3.Response intercept(Chain chain) throws IOException {okhttp3.Response originalResponse = chain.proceed(chain.request());return originalResponse.newBuilder().body(new ProgressResponseBody(originalResponse.body(), progressListener)).build();}});return builder;}

上述代码中在添加拦截器时通过自定义的ResponseBody将下载的进度信息回调了出来,并通过Handler不断的将下载进度发送出去。ProgressResponseBody中代码如下:

public class ProgressResponseBody extends ResponseBody {private final ResponseBody responseBody;private final ProgressListener progressListener;private BufferedSource bufferedSource;public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {this.responseBody = responseBody;this.progressListener = progressListener;}@Overridepublic MediaType contentType() {return responseBody.contentType();}@Overridepublic long contentLength() {return responseBody.contentLength();}@Overridepublic 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;@Overridepublic long read(Buffer sink, long byteCount) throws IOException {long bytesRead = super.read(sink, byteCount);totalBytesRead += bytesRead != -1 ? bytesRead : 0;progressListener.onProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1);return bytesRead;}};}
}

2.为Retrofit添加进度监听。 上面我们已经实现了下载进度的获取,那么接下来就需要为Retrofit设置下载进度监听了。来看DownloadUtils中的代码:

 public void download(@NonNull String url, DownloadListener downloadListener) {mDownloadListener = downloadListener;getApiService().download(url).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).doOnNext(getConsumer()).observeOn(AndroidSchedulers.mainThread()).subscribe(getObserver());}private CommonService getApiService() {OkHttpClient.Builder httpClientBuilder = RetrofitService.getOkHttpClientBuilder();ProgressHelper.addProgress(httpClientBuilder);CommonService commonService = RetrofitService.getRetrofitBuilder(Constants.API_SERVER_URL).client(httpClientBuilder.build()).build().create(CommonService.class);ProgressHelper.setProgressHandler(new DownloadProgressHandler() {@Overrideprotected void onProgress(long bytesRead, long contentLength, boolean done) {mDownloadListener.onProgress((int) ((100 * bytesRead) / contentLength));}});return commonService;}

上面代码中将httpClientBuilder添加到ProgressHelper中,然后调用ProgressHelper中的setProgressHandler方法回调除了下载进度信息。此时通过DownloadListener类再次将下载信息回调了出来。同时DownloadListener中还有onSuccess、onFail、onComplete方法,分别在以下地方调用:

private Observer<ResponseBody> getObserver() {return new Observer<ResponseBody>() {@Overridepublic void onSubscribe(Disposable d) {mDisposables.add(d);}@Overridepublic void onNext(ResponseBody responseBody) {mDownloadListener.onComplete();}@Overridepublic void onError(Throwable e) {mDownloadListener.onFail(e.getMessage());}@Overridepublic void onComplete() {mDownloadListener.onComplete();}};}

3.取消下载。取消下载其实就是通过CompositeDisposable中的clear方法终止了RxJava的生命周期。

至此关于Retrofit的上传与下载就分析完了,文末可以参看源码。

(一)Rxjava2+Retrofit完美封装
(二)Rxjava2+Retrofit实现Token自动刷新
(三)Rxjava2+Retrofit实现文件上传与下载

源码传送门

好库推荐

给大家推荐一下BannerViewPager。这是一个基于ViewPager实现的具有强大功能的无限轮播库。通过BannerViewPager可以实现腾讯视频、QQ音乐、酷狗音乐、支付宝、天猫、淘宝、优酷视频、喜马拉雅、网易云音乐、哔哩哔哩等APP的Banner样式以及指示器样式。

欢迎大家到github关注BannerViewPager!

(三)Rxjava2+Retrofit之文件上传与下载相关推荐

  1. Retrofit实现文件上传(二)

    1.序言 这几天一直在忙其他乱七八糟的事情,都没能好好敲我的代码呢?昨天,在大力大神的指点下,我对Retrofit的文件上传开悟了:在上节的解析中,我只是蜻蜓点水的提了一下Retrofit的文件上传, ...

  2. Python实现阿里云aliyun服务器里的文件上传与下载

    Python实现阿里云服务器里的文件上传与下载 Python实现阿里云服务器里的文件上传与下载 背景: 正文: 预备环境: 构想: 实现: 注意: 结尾 018.4.15 背景: 老实说,因为现实的各 ...

  3. WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传、下载

    MTOM消息优化传输机制主要应用于大量数据的传输,很多文章中也直接得出结论:使用MTOM文件传输效率高.为什么MTOM的数据传输效率会比别的方式要高?MTOM真的如此完美吗,它有什么不足?什么情况下使 ...

  4. 科普|不同协议下远程服务器文件上传_下载优劣对比

    作为一个程序员,如果不知道如何进行远程服务器的文件上传与下载,实在是一件尴尬的事情.打开百度,搜索「远程服务器 上传下载」,你能得到 63,100,000 个搜搜结果,五花八门的操作方式的让人眼花缭乱 ...

  5. 一篇文章教你学会使用SpringBoot实现文件上传和下载

    文章目录 一.搭建SpringBoot开发环境 1.创建项目 2.配置application.properties参数 3.实体响应类和异常信息类 4.创建FileController 二.接口测试 ...

  6. python实现文件上传和下载_[Python] socket实现TFTP上传和下载

    一.说明 本文主要基于socket实现TFTP文件上传与下载. 测试环境:Win10/Python3.5/tftpd64. tftpd下载:根据自己的环境选择下载,地址 :http://tftpd32 ...

  7. JavaWeb学习总结(五十)——文件上传和下载

    在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用 ...

  8. java里面的文件上传与下载

    java里面的文件上传与下载 文件的上传与下载主要用到两种方法: 1.方法一:commons-fileupload.jar commons-io.jar apache的commons-fileuplo ...

  9. layui上传文件请求接口异常_SpringMVC实现文件上传与下载,拦截器,异常处理

    第一章:响应数据和结果视图 1. 返回字符串 Controller方法返回字符串可以指定逻辑视图的名称,根据视图解析器为物理视图的地址. @RequestMapping(value="/he ...

最新文章

  1. 那天,我被拉入一个Redis群聊···
  2. PICRUSt:预测宏基因组功能—16S扩增子分析锦上添花
  3. 开发中经常使用的5种设计模式
  4. 谷歌Android无障碍套件,谷歌为无障碍套件添加盲文键盘:无需额外硬件就能打字...
  5. 中国口内X射线系统行业市场供需与战略研究报告
  6. GMA Round 1 三视图
  7. 最大似然估计、MAP及贝叶斯估计
  8. Codeforces Gym 100187E E. Two Labyrinths bfs
  9. 发送邮件 空格 java_java实现邮件发送功能
  10. 金士顿16g u盘量产
  11. 中文 tts 开源 Android,7 个开源的TTS(文本转语音)系统推荐-Go语言中文社区
  12. Java程序员面试时,如何进行自我介绍呢?
  13. 转载:Apache Ignite(一):简介以及和Coherence、Gemfire、Redis等的比较
  14. srand((unsigned int)time(NULL))的理解(C语言)
  15. 从头撸到脚,SpringBoot 就一篇全搞定!
  16. 这些业务,正在帮助百度跳出搜索框
  17. 基于星环TDH数据仓库典型总和场景数据流转设计
  18. Roblox剑九之剑一
  19. Windows关于文件句柄数的限制
  20. java判断是否是数组_Java判断对象类型是否为数组

热门文章

  1. 一刀工具箱 - 动物叫声
  2. 网络 | 网络协议与网络制式
  3. web菜单的实现【zt】
  4. 苹果手机云便签新增内容无法使用中文输入法输入汉字怎么办?
  5. Redis的慢查询会导致连接池耗尽和No more cluster attempts left.的告警
  6. kinit: Included profile directory could not be read while initializing Kerberos 5 library
  7. oracle对同义词的解释,[原创]oracle同义词解释
  8. P5117 [USACO18DEC]The Bucket List 题解
  9. 使用OTL操作Oracle数据库
  10. 毕业生迷茫,中年人焦虑,职场路如何越走越宽?