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 默认赋值,这里才是关键。

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

时间: 2024-10-17 22:24:33

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

移动端html5图片上传方法【更好的兼容安卓IOS和微信】_Android

之前的移动端上传的方法,有些朋友测试说微信支持不是很好,还有部分安卓机也不支持,其实我已经有了另一个方法,但是例子还没整理出来,而联系我的很多朋友需要,所以就提前先发出来了,并且做一个简单的说明,就不做一个demo了. <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=dev

苹果开发之ios图片上传方法

方法1 在项目中我们经常会遇到需要上传图片的地方,比如更换头像,上传证件照片等.下面介绍一种上传图片的方法. 首先我们需要在项目里打开手机的相册或者相机,然后在下面这个代理方法里进行图片的上传操作. 需要遵循 <UIImagePickerControllerDelegate,UINavigationControllerDelegate>代理. -(void)imagePickerController:(UIImagePickerController *)picker didFinishPick

移动端html5图片上传方法【更好的兼容安卓IOS和微信】

之前的移动端上传的方法,有些朋友测试说微信支持不是很好,还有部分安卓机也不支持,其实我已经有了另一个方法,但是例子还没整理出来,而联系我的很多朋友需要,所以就提前先发出来了,并且做一个简单的说明,就不做一个demo了. <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=dev

asp.net图片上传方法

上传图片的相对路径到数据库教程中相应字段里,读取显示时,将控件(假设用的是image控件)的imageurl属性指向该相对路径即可. code: protected void button1_click(object sender, eventargs e)   {   string name = fileupload1.filename;//获取文件名   string type = name.substring(name.lastindexof(".") + 1);       /

教你轻松玩转Picasa图片上传

长假归来,相信不少朋友都带回了很多漂亮的假期照片.不过,独乐不如群乐,您是否想过要将自己假期中的所见所闻与朋友们一同分享呢?其实,最简单的一个办法,就是将照片上传到网络相册.不过,如何上传效率才是最高?却是一个说简单就简单,说复杂也复杂的问题.那么下面,笔者就以Google Picasa网络相册为例,向大家介绍几种常见的照片上传方法. 一. ActiveX控件上传 * 优点:可同时上传多张图片,且上传列表可被任意编辑­.同时,我们也能清楚地看到图片上传进度. * 缺点:需要事先安装ActiveX

c# 客户端 调用 java Webservice 实现图片上传功能

问题描述 c# 客户端 调用 java Webservice 实现图片上传功能 no SOAPAction header c#端添加服务引用 生成实体类 直接调用webservice图片上传方法 结果报错**no SOAPAction header** 解决方案 有没有大神 来帮帮忙 解决方案二: 参考这个:http://blog.csdn.net/wxyong3/article/details/38727503

as 3.0图片上传预览本地图片

需要Flash Player 10+版本的支持, [注意]: 1.我这边图片上传路径是无效的,所以图片上传失败是正常的,你们可以改一下上传路径即可: 2.需要Flash Player 10的支持: 3.这次主要研究是预览本地图片功能. 演示效果: 实现代码: view plaincopy to clipboardprint? package project.test     {        import flash.display.*;        import flash.geom.Rec

android5 0-Android5.0调试 图片上传

问题描述 Android5.0调试 图片上传 这个是什么问题,系统在4.4上无问题,在5.0上报错了,完成的是一个图片上传的功能 解决方案 android图片上传android 上传图片

接口-php CURLOPT_POSTFIELDS的绝对路径参数怎样填写?(因为图片上传得不到绝对路径)

问题描述 php CURLOPT_POSTFIELDS的绝对路径参数怎样填写?(因为图片上传得不到绝对路径) 利用微信的上传图片接口写一个php文件. HTML代码如下:<input type="file" name="file" value="123"/> <br /> <input type="submit" name="submit" value="submit

服务器那边没数据-iOS 图片上传,服务器那边收不到数据,显示字节数为0

问题描述 iOS 图片上传,服务器那边收不到数据,显示字节数为0 /** 上传头像 接口说明 此接接口以POST方式请求. 请求说明http://124.207.188.52/firmail/app/imgupload/upload?uid=111&file=图片二进制流 参数说明 uid 用户id file 图片的二进制流 */ (void)asiUploadIcon { NSURL *url = [NSURL URLWithString:@"http://124.207.188.52