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),不过后⾯对RequestBod
y的处理⽅式也要跟着变化,这⾥就不详细说了,只会介绍上⾯这种简便清晰的⽅式。
⼆、初始化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)
.ate())
.ate())
.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"));
android retrofit//为了构建数据,同样是伪代码
String path1 = ExternalStorageDirectory() + File.separator + "test1.jpg";
String path2 = ExternalStorageDirectory() + 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);
.postGoodsReturnPostEntitys(params,partList)
.wThread())
.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 = ate(MediaType.parse("text/plain"), param);
return requestBody;
}
因为ate()转换器的缘故,会将参数请求头的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 = ate(MediaType.parse("image/png"), file);
MultipartBody.Part part = ateFormData("multipartFiles", Name(), requestBody);
parts.add(part);
}
return parts;
}
说到底,还是对参数请求头和⽂件(图⽚)请求头的content-type属性赋值处理,不要让Retrofit 默认赋值,这⾥才是关键。以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论