Retrofit2.0 实现图文(参数+图片)上传方法总结

 更新时间:2017年08月03日 09:57:42   作者:墨源  
本篇文章主要介绍了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 默认赋值,这里才是关键。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Android实战教程第四十三篇之上拉加载与下拉刷新

    Android实战教程第四十三篇之上拉加载与下拉刷新

    这篇文章主要为大家详细介绍了Android实战教程第四十三篇之上拉加载与下拉刷新,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • Android设置当TextView中的文字超过TextView的容量时用省略号代替

    Android设置当TextView中的文字超过TextView的容量时用省略号代替

    这篇文章主要介绍了Android设置当TextView中的文字超过TextView的容量时用省略号代替 ,需要的朋友可以参考下
    2017-03-03
  • Android TextView渐变颜色和方向及动画效果的设置详解

    Android TextView渐变颜色和方向及动画效果的设置详解

    TextView的在安卓中可以理解为一个文本视图控件,Android的视图控件的基类是View类,可以理解的TextView是View的子类。我们通常在.XML布局文件中会为文本视图控件指定各种属性来设置它的样式,今天我们要讲的当然不是传统常见的那种,将会带有渐变颜色和方向及动画效果
    2021-11-11
  • 分享五种Android常用布局方式

    分享五种Android常用布局方式

    Android布局是应用界面开发的重要一环,在Android中,共有五种布局方式,分别是:FrameLayout(框架布 局),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局),小编通过本文逐一给大家详解
    2015-11-11
  • Android性能优化之弱网优化详解

    Android性能优化之弱网优化详解

    这篇文章主要为大家介绍了Android性能优化之弱网优化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Android自定义LinearLayout布局显示不完整的解决方法

    Android自定义LinearLayout布局显示不完整的解决方法

    这篇文章主要给大家介绍了关于Android自定义LinearLayout但布局显示不完整的解决方法,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-11-11
  • Android仿360悬浮小球自定义view实现示例

    Android仿360悬浮小球自定义view实现示例

    本篇文章主要介绍了Android仿360悬浮小球自定义view实现示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-03-03
  • Android创建Menu菜单实例

    Android创建Menu菜单实例

    这篇文章主要介绍了Android创建Menu菜单实例,讲述了Android菜单项的创建方法,在Android应用程序开发中非常具有实用价值,需要的朋友可以参考下
    2014-10-10
  • Android NDK开发(C语言--联合体与枚举)

    Android NDK开发(C语言--联合体与枚举)

    这篇文章主要介绍了Android NDK开发C语言联合体与枚举,共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。下面详细介绍该内容,需要的朋友可以参考一下
    2021-12-12
  • Android编程获取APP应用程序基本信息辅助类【APP名称、包名、图标,版本号等】

    Android编程获取APP应用程序基本信息辅助类【APP名称、包名、图标,版本号等】

    这篇文章主要介绍了Android编程获取APP应用程序基本信息辅助类,可实现针对APP名称、包名、图标,版本号等信息的获取功能,需要的朋友可以参考下
    2017-12-12

最新评论