这里有新鲜出炉的精品教程,程序狗速度看过来!
Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。
本篇文章主要介绍了Retrofit2.0 实现图文(参数+图片)上传方法总结,具有一定的参考价值,有兴趣的可以了解一下
最近项目里用到了类似图文上传的功能,以前都是封装OkHttp的文件上传功能,这次想换个姿势,想用Retrofit2.0实现这样的功能,本来以为挺简单的,没想到进入了深坑,连续调整了好几种姿势都报了同一个错,接着网上类似的文章找了一大推,讲得都是模棱两可,或者对多参数格式不够友好,最后还是去看了相关的源码,自己把这个问题提出来解决了,在这里记录一下。
一、定义网络请求接口
- public interface GoodsReturnApiService {
- @Multipart
- @POST(Compares.GOODS_RETURN_POST) //这里是自己post文件的地址
- Observable<GoodsReturnPostEntity> postGoodsReturnPostEntitys(@PartMap Map<String, RequestBody> map, @Part List<MultipartBody.Part> parts);
- }
上面定义了一个接口用于上传文件请求,有几个注解需要说明一下, @Multipart这是Retrofit专门用于文件上传的注解,需要配合@POST一起使用。
方法postGoodsReturnPostEntitys(@PartMap Map<String, RequestBody> map, @Part List<MultipartBody.Part> parts)第一个参数使用注解@PartMap用于多参数的情况,如果是单个参数也可使用注解@Part。
在类型Map<String, RequestBody>中,Map第一个泛型String是服务器接收用于文件上传参数字段的Key,第二个泛型RequestBody是OkHttp3包装的上传参数字段的Value,这也是图文上传成功的关键所在。在后面会具体说到。
第二个参数使用注解@Part用于文件上传,多文件上传使用集合类型List<MultipartBody.Part>,单文件可以使用类型MultipartBody.Part,具体的使用同样后面讲。
这里着重说明一下,postGoodsReturnPostEntitys(@PartMap Map<String, RequestBody> map, @Part List<MultipartBody.Part> parts)方法参数这样写纯属个人习惯,你也可以直接使用一个参数postGoodsReturnPostEntitys(@PartMap Map<String, RequestBody> map),不过后面对RequestBody的处理方式也要跟着变化,这里就不详细说了,只会介绍上面这种简便清晰的方式。
二、初始化Retrofit
- public class HttpRequestClient {
- public static final String TAG = "HttpRequestClientTAG";
- private static Retrofit retrofit;
- private static OkHttpClient getOkHttpClient() {
- //日志显示级别
- HttpLoggingInterceptor.Level level= HttpLoggingInterceptor.Level.BODY;
- //新建log拦截器
- HttpLoggingInterceptor loggingInterceptor=new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
- @Override
- public void log(String message) {
- Log.d(TAG, message);
- }
- });
- loggingInterceptor.setLevel(level);
- //定制OkHttp
- OkHttpClient.Builder httpClientBuilder = new OkHttpClient
- .Builder();
- //OkHttp进行添加拦截器loggingInterceptor
- httpClientBuilder.addInterceptor(loggingInterceptor);
- return httpClientBuilder.build();
- }
- public static Retrofit getRetrofitHttpClient(){
- if(null == retrofit){
- synchronized (HttpRequestClient.class){
- if(null == retrofit){
- retrofit = new Retrofit.Builder()
- .client(getOkHttpClient())
- .baseUrl(Compares.URL)
- .addConverterFactory(GsonConverterFactory.create())
- .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
- .build();
- }
- }
- }
- return retrofit;
- }
- }
为了演示,Retrofit封装比较简陋,为的是查看网络拦截,就不详细说了。
三、发起文件上传请求
- private void postGoodsPicToServer(){
- Map<String,RequestBody> params = new HashMap<>();
- //以下参数是伪代码,参数需要换成自己服务器支持的
- params.put("type", convertToRequestBody("type"));
- params.put("title",convertToRequestBody("title"));
- params.put("info",convertToRequestBody("info");
- params.put("count",convertToRequestBody("count"));
- //为了构建数据,同样是伪代码
- String path1 = Environment.getExternalStorageDirectory() + File.separator + "test1.jpg";
- String path2 = Environment.getExternalStorageDirectory() + File.separator + "test1.jpg";
- List<File> fileList = new ArrayList<>();
- fileList.add(new File(path1));
- fileList.add(new File(path2));
- List<MultipartBody.Part> partList = filesToMultipartBodyParts(fileList);
- HttpRequestClient.getRetrofitHttpClient().create(GoodsReturnApiService.class)
- .postGoodsReturnPostEntitys(params,partList)
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Observer<GoodsReturnPostEntity>() {
- @Override
- public void onSubscribe(@NonNull Disposable d) {
- }
- @Override
- public void onNext(@NonNull GoodsReturnPostEntity goodsReturnPostEntity) {
- }
- @Override
- public void onError(@NonNull Throwable e) {
- }
- @Override
- public void onComplete() {
- }
- });
- }
上面的params和fileList都是构造的伪代码,需要根据自己项目的业务需求改变。
下面是上传文件成功第一个关键,对参数请求头(姑且叫这个名字,对应Retrofit上传文件时参数那部分请求头,下文件(图片)请求头同理,对应文件那部分请求头)的content-type赋值,使用convertToRequestBody()方法。
- private RequestBody convertToRequestBody(String param) {
- RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), param);
- return requestBody;
- }
因为GsonConverterFactory.create()转换器的缘故,会将参数请求头的content-type值默认赋值application/json,如果没有进行这步转换操作,就可以在OKHttp3的日志拦截器中查看到这样的赋值,这样导致服务器不能正确识别参数,导致上传失败,所以这里需要对参数请求头的content-type设置一个正确的值:text/plain。
下面是上传文件成功第二个关键的地方,将文件(图片)请求头的content-type使用方法filesToMultipartBodyParts()对其赋值"image/png",并返回MultipartBody.Part集合。
- private List < MultipartBody.Part > filesToMultipartBodyParts(List < File > files) {
- List < MultipartBody.Part > parts = new ArrayList < >(files.size());
- for (File file: files) {
- RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
- MultipartBody.Part part = MultipartBody.Part.createFormData("multipartFiles", file.getName(), requestBody);
- parts.add(part);
- }
- return parts;
- }
说到底,还是对参数请求头和文件(图片)请求头的content-type属性赋值处理,不要让Retrofit 默认赋值,这里才是关键。
来源: http://www.phperz.com/article/17/0913/345884.html