关于使用retrofit上传图片, 网上有许多教程, 再次做一下粗略的总结.
上传图片为post
请求, 先写接口, 一般来说有下面四种写法:
@Multipart
@POST("user/imgUpLoad")
//1
//Call<String> uploadOne(@Part("sign") String sign,@Part("appKey") String appKey,@Part("osName") String osName,@Part("memberNo") String memberNo, @Part MultipartBody.Part file);
//2
//Call<String> uploadOne(@PartMap Map<String,String> params, @Part MultipartBody.Part file);
//3
//Call<String> uploadOne(@Query("sign") String sign, @Query("appKey") String appKey, @Query("osName") String osName, @Query("memberNo") String memberNo, @Part MultipartBody.Part file);
//4
Call<String> uploadOne(@QueryMap Map<String,String> params, @Part MultipartBody.Part file);
1和2这种写法本质是一样的, @Part后面的参数, 都是放在请求体中.
3和4则是另一种方式的同一写法, @Query后面的参数回拼接在url后面.
在上传图片之前, 我先在postman中请求了一下, 测试一下参数是否都正确
按照后台提供的接口和参数, 一切正常.
ok, 下面继续回到代码中
- retrofit接口:
@Multipart
@POST("pronline/upload")
Call<ResponseBody> upImg(@PartMap Map<String,String> params,@Part MultipartBody.Part file);
- 构建图片body:
String fileNameByTimeStamp = FormatUtil.getTimeStamp() + ".jpg";
File file = new File(imgPath);
RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpeg"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData("app_user_header", fileNameByTimeStamp, requestFile);
- 构建参数map
Map<String, String> params = new HashMap<>();
params.put("FunName", "ict_uploadpicture");
params.put("path", "/uploadNews");
params.put("appfile", fileNameByTimeStamp);
- 最后上传
HttpUtils.getInstance().getAPI()
.upImg(params, body)
.enqueue(new Callback<ResponseBody>() {
...
}
一般来说到这里上传图片解成功了.
然并卵, 服务器无情的给我返回了 500 Internal Server Error
,表示服务器内部错误,证明我们传过去的东东,后台处理的时候发生异常。(这里吐槽下后台的哥们没有catch处理下异常给个提示)
明明在postman中可以正常上传的...
于是我用Wireshark对postman和retrofit上传图片进行抓包
Wireshark过滤ip规则:ip.dst==192.168.1.1
结果如下:
盲僧, 我发现了华点, 看到了没有, 红框部分, retrofit请求的content-type
为
application/json
, 而postman的请求则没有content-type参考:http://blog.csdn.net/zjm0518/article/details/60781238
既然发现问题就好办了, 直接把content-type给去掉呗.
- 接口
@Multipart
@POST("pronline/upload")
Call<ResponseBody> upImg(@Part("FunName") RequestBody funName,
@Part("path") RequestBody path,
@Part("appfile") RequestBody appfile,
@Part MultipartBody.Part file);
- 上传图片
//funName
RequestBody funName = RequestBody.create(null, "ict_uploadpicture");
//path
RequestBody path = RequestBody.create(null, "/uploadNews");
//appfile
RequestBody appfile = RequestBody.create(null, fileNameByTimeStamp);
//上传图片的名字
String fileName = FormatUtil.getTimeStamp() + ".jpg";
//图片文件
File file = new File(imgPath);
RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpeg"), file);
/**
*file部分,最终拼接成以下部分(注意“app_user_header”是后台定义好的,后台会用它作为key去查询你传的图片信息):
*Content-Disposition: form-data; name="app_user_header"; filename=fileNameByTimeStamp
*Content-Type: image/jpeg
*Content-Length: 52251(图片流字节数组的长度,底层的Okhttp帮我们计算了)
*...(文件流)
*/
MultipartBody.Part body = MultipartBody.Part.createFormData("app_user_header", fileName, requestFile);
关键是这句RequestBody.create(null, "ict_uploadpicture");
第一个参数传入MediaType
,
直接传null, 表示content-type
为空.
-- END