Retrofit 上传图片的坑
TIP 不是原创,只是记录一下 ,侵删!
内容
一、使用Retrofit上传文件时遇到的坑。项目中注册接口中有上传头像的功能,本以为上传头像是一个很简单的事情,可万万没想到使用Retrofit上传头像时却遇到了一个大坑。填这个坑花费了足足两天的时间。
先来看下上传文件时遇到的异常信息:
java.lang.IllegalArgumentException: Invalid % sequence at 432: --8c46470b-77e7-4dd8-b4e5-55f6968db182
Content-Disposition: form-data; name="phone"
Content-Length: 111551526--8c46470b-77e7-4dd8-b4e5-55f6968db182
Content-Disposition: form-data; name="password"
Content-Length: 6123123--8c46470b-77e7-4dd8-b4e5-55f6968db182
Content-Disposition: form-data; name="uploadFile"; filename="test.txt"
Content-Type: multipart/form-dataContent-Length: 27
Hello World %--8c46470b-77e7-4dd8-b4e5-55f6968db182--
分析上面的日志可以知道,错误的原因是因为文件中存在非法的参数“%”。什么情况啊,难道上传文件还不能包含“%”了这不科学啊。况且试验了图片上传每张图片流中都包含%。也就是所有图片上传均失败。于是自己又写了一个test.txt
的文件放到了手机里边测试,当test.txt文件中写入Hello World时候测试上传没有
异常,但当我在Hello World末尾加了%后就出现了java.lang.IllegalArgumentException: Invalid % sequence at 432
这个异常!刚开始的时候以为是自己上传文件部分代码写的有问题。于是参考网上的代码试了各种上传方法均无济于事。接着开始百度看网上是否有类似问题,但是几乎搜遍了整个百度也没有找到问题的解决方案。后来转战谷歌,强大的谷歌上也没有找到解决办法!无奈之下只好自己调了,于是开始逐一排查自己封装的代码是否有问题。经过不懈的努力终于找到了问题所在。
请看下面代码
public class RetrofitUtils {
private static final long DEFAULT_TIMEOUT = 10;
private volatile static RetrofitUtils apiEngine;
private Retrofit retrofit;
private RetrofitUtils() {
//日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor((String message) -> {
try {
String text = URLDecoder.decode(message, "utf-8");
Log.e("OKHttp-----", text);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Log.e("OKHttp-----", message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
//缓存
int size = 1024 * 1024 * 100;
File cacheFile = new File(App.getContext().getCacheDir(), "OkHttpCache");
Cache cache = new Cache(cacheFile, size);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.addNetworkInterceptor(new NetworkInterceptor())
.addInterceptor(loggingInterceptor)
.cache(cache)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(Contact.BASE_URL)
.client(client)
//然后将下面的GsonConverterFactory.create()替换成我们自定义的ResponseConverterFactory.create()
// .addConverterFactory(ResponseConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
public static RetrofitUtils getInstance() {
if (apiEngine == null) {
synchronized (RetrofitUtils.class) {
if (apiEngine == null) {
apiEngine = new RetrofitUtils();
}
}
}
return apiEngine;
}
public ApiService getApiService() {
return retrofit.create(ApiService.class);
}
}
这段代码是对Retrofit进行封装并添加了拦截器,当我把 .addInterceptor(loggingInterceptor)这一行注释掉以后,奇迹出现了。点击注册后竟然神奇般的注册成功了!(发现了问题所在,兴奋至极啊,真的就差一点就放弃了!)注意这行代码,是为Retrofit添加了日志拦截器!继续跟进代码看这个拦截器的内容,经过调试发现竟然是因为HttpLoggingInterceptor 中setLevel
的一行代码导致的,即loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
这行代码。于是试了试把BOEY改成了HEADERS后再试测发现这个异常就没有再出现了!但是改过之后日志拦截器会有些问题,就是只能拦截请求头的日志,请求体中的日志无法拦截,其实已经失去了拦截日志的意义了!但是这个问题终归解决了。只需要改一个单词或者去掉日志拦截器!修改后是这样的:loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);