(三)最流行的网络请求框架Rxjava2+Retrofit之文件上传
来源:互联网 发布:淘宝带图评价福利 编辑:程序博客网 时间:2024/04/30 14:42
* 本文同步发表在简书,转载请注明出处。
结合 第一篇文章对Retrofit的封装,本篇文章将讲述如何实现文件上传服务器。要讲述的内容主要有一下几点:
- 使用Retrofit上传文件时遇到的坑
- 实现单文件上传
- 实现多文件上传
一、使用Retrofit上传文件时遇到的坑。项目中注册接口中有上传头像的功能,本以为上传头像是一个很简单的事情,可万万没想到使用Retrofit上传头像时却遇到了一个大坑。填这个坑花费了足足两天的时间。先来看下上传文件时遇到的异常信息:
java.lang.IllegalArgumentException: Invalid % sequence at 432: --8c46470b-77e7-4dd8-b4e5-55f6968db182Content-Disposition: form-data; name="phone"Content-Length: 111551526--8c46470b-77e7-4dd8-b4e5-55f6968db182Content-Disposition: form-data; name="password"Content-Length: 6123123--8c46470b-77e7-4dd8-b4e5-55f6968db182Content-Disposition: form-data; name="uploadFile"; filename="test.txt"Content-Type: multipart/form-dataContent-Length: 27Hello World %--8c46470b-77e7-4dd8-b4e5-55f6968db182--
分析上面的日志可以知道,错误的原因是因为文件中存在非法的参数“%”。什么情况啊,难道上传文件还不能包含“%”了这不科学啊。况且试验了图片上传每张图片流中都包含%。也就是所有图片上传均失败。于是自己又写了一个test.txt
的文件放到了手机里边测试,当test.txt文件中写入Hello World时候测试上传没有
异常,但当我在Hello World末尾加了%后就出现了java.lang.IllegalArgumentException: Invalid % sequence at 432
这个异常!刚开始的时候以为是自己上传文件部分代码写的有问题。于是参考网上的代码试了各种上传方法均无济于事。接着开始百度看网上是否有类似问题,但是几乎搜遍了整个百度也没有找到问题的解决方案。后来转战谷歌,强大的谷歌上也没有找到解决办法!无奈之下只好自己调了,于是开始逐一排查自己封装的代码是否有问题。经过不懈的努力终于找到了问题所在。请看下面代码
private IdeaApi() { // 日志拦截器 HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor((message) -> { try { String text = URLDecoder.decode(message, "utf-8"); LogUtils.e("OKHttp-----", text); } catch (UnsupportedEncodingException e) { e.printStackTrace(); LogUtils.e("OKHttp-----", message); } }); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); File cacheFile = new File(Utils.getContext().getCacheDir(), "cache"); Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb LogUtils.e(UserInfoTools.getUserId(Utils.getContext())); OkHttpClient okHttpClient = new OkHttpClient.Builder() .readTimeout(IdeaApiService.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS) .connectTimeout(IdeaApiService.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS) .addNetworkInterceptor(new HttpCacheInterceptor()) .cache(cache) .addInterceptor((chain) -> { // 统一配置配置请求头 Request request = chain.request().newBuilder() .addHeader("user_id", UserInfoTools.getUserId(Utils.getContext())) .addHeader("psw_just_test", "4567") .build(); return chain.proceed(request); }) .addInterceptor(loggingInterceptor) .build(); Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create(); Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(IdeaApiService.API_SERVER_URL) .build(); service = retrofit.create(IdeaApiService.class); }
这段代码是对Retrofit进行封装并添加了拦截器,当我把 .addInterceptor(loggingInterceptor)这一行注释掉以后,奇迹出现了。点击注册后竟然神奇般的注册成功了!(发现了问题所在,兴奋至极啊,真的差一点就放弃了!)注意这行代码,是为Retrofit添加了日志拦截器!继续跟进代码看这个拦截器的内容,经过调试发现竟然是因为HttpLoggingInterceptor 中setLevel
的一行代码导致的,即loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
这行代码。于是试了试把BOEY改成了HEADERS后再试测发现这个异常就没有再出现了!但是改过之后日志拦截器会有些问题,就是只能拦截请求头的日志,请求体中的日志无法拦截,其实已经失去了拦截日志的意义了!但是这个问题终归解决了。只需要改一个单词或者去掉日志拦截器!修改后是这样的:loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
二 、实现单文件上传的两种方法
解决了上面的异常后心里一下再踏实了好多!于是接下来看看如何使用Retrofit来上传文件把!假如说我们注册时接口有三个参数:手机号、密码、头像。
1.单文件上传方法(1)
首先定义注册接口方法:
@Multipart @POST("user/register.do") Observable<BasicResponse<RegisterBean>> register(@Part("phone") RequestBody phone,@Part("password") RequestBody password,@Part MultipartBody.Part image);
注意上面上面方法加了@Multipart的注解。对于上传文件必须要加这个注解,不然会报异常!另外方法中有三个参数,即我们注册时所需要的参数!返回值是我我们自定义的Observable(详见上一片文章),接下来在我们注册的页面调用这个方法,如下:
File file = new File(picPath); // 图片参数RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); MultipartBody.Part body = MultipartBody.Part.createFormData("uploadFile", file.getName(), requestFile); // 手机号参数RequestBody phoneBody = RequestBody.create(MediaType.parse("multipart/form-data"), phone);// 密码参数RequestBody pswBody = RequestBody.create(MediaType.parse("multipart/form-data"), password);IdeaApi.getApiService() .register(phoneBody,pswBody,body) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DefaultObserver<BasicResponse<RegisterBean>>(this, true) { @Override public void onSuccess(BasicResponse<RegisterBean> response) { EventBus.getDefault().post(new RegisterSuccess("register success")); showToast("注册成功,请登陆"); finish(); } });
显然,上面的方法有个弊端。当我们接口中需要的参数较少时使用上面的方法无可厚非。但假如说我们接口中需要的参数非常多,那么上面的方法使用起来就麻烦了。因此接下来我们可以看下第二种方法如何使用。
2.单文件上传方法(2),同样以注册接口为例,先定义注册接口的方法:
@Multipart @POST("user/register.do") Observable<BasicResponse<RegisterBean>> register(@Part List<MultipartBody.Part> partList);
可以发现方法中的参数变成了List《MultipartBody.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("phone", phone) .addFormDataPart("password", password) .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<BasicResponse<RegisterBean>>(this, true) { @Override public void onSuccess(BasicResponse<RegisterBean> response) { EventBus.getDefault().post(new RegisterSuccess("register success")); showToast("注册成功,请登陆"); finish(); }
这样是不是比第一种方法清爽了很多呢!
三、 实现多文件上传。
对于多图上传其实跟单文件上传没有多大区别,只不过多了些参数而已。跟但图片类似同样可以有两种方法。
1.多文件传方法(1):
首先定义多文件上传接口:
@POST("upload/") Observable<BasicResponse> 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<BasicResponse<RegisterBean>>(this, true) { @Override public void onSuccess(BasicResponse<RegisterBean> response) { EventBus.getDefault().post(new RegisterSuccess("register success")); showToast("注册成功,请登陆"); finish(); }
或者使用第二种方法实现多文件上传,如下:
1.多文件上传方法(2),采用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("文件1",requestFile1 ); map.put("文件2",requestFile2 ); IdeaApi.getApiService() .uploadFiles("pictures",map) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DefaultObserver<BasicResponse<RegisterBean>>(this, true) { @Override public void onSuccess(BasicResponse<RegisterBean> response) { EventBus.getDefault().post(new RegisterSuccess("register success")); showToast("注册成功,请登陆"); finish(); }
至此关于Retrofit上传文件已经基本完成。但由于多文件上传并没有实际验证,因此如果存在问题请留言告知。
- (三)最流行的网络请求框架Rxjava2+Retrofit之文件上传
- (二)最流行的网络请求框架Rxjava2+Retrofit之Token处理
- 最流行的网络请求框架Rxjava2+Retrofit完美封装
- (一)最流行的网络请求框架Rxjava2+Retrofit完美封装
- Android网络请求框架之Retrofit(三)
- 快捷使用Retrofit网络请求框架(get请求,post请求,上传文件)
- rxjava2 与 retrofit 网络请求
- (安卓) MVP 框架 (Rxjava2+Retrofit)结合 实现网络请求
- rxjava2 + retrofit上传文件示例
- Android/Java网络加载框架Retrofit(三)结合RxJava2与RxAndroid2
- Retrofit2+Rxjava2网络请求框架的封装
- Android网络请求框架之Retrofit(一)
- Android网络请求框架之Retrofit(二)
- Android网络请求框架之Retrofit实践
- Android 网络请求框架之Rxjava+Retrofit
- Android 网络请求框架之Rxjava+Retrofit
- Android 网络请求框架之Rxjava+Retrofit
- Android 网络请求框架之Rxjava+Retrofit
- Failed to start LSB: Bring up/down networking.
- 反而价格; 大姑家减肥
- 我的博客的第一天,从此进入前端的人生轨迹/
- 今天所学到的一些东西
- XSS常见攻击与防御
- (三)最流行的网络请求框架Rxjava2+Retrofit之文件上传
- [BZOJ1925][Sdoi2010]地精部落(抖动序列dp)
- SVD 奇异值分解( The singular value decomposition )
- mysql高级排序&高级匹配查询示例
- hibernate自动建表时的错误解决办法
- CodeForces
- BM25算法 仅供自己记录学习
- apache 转发到tomcat
- 笔记本电脑数据丢失怎么恢复