java.lang.IllegalArgumentException: Unexpected char 0x5a46 at 35 in Content-Disposition value: form-data; name=“files”; filename=“婆婆说.mp3”
问题描述
使用Retrofit +okhttp上传文件时,当文件名称含有中文字符时,如下错误
java.lang.IllegalArgumentException: Unexpected char 0x5a46 at 35 in Content-Disposition value: form-data; name="files"; filename="婆婆说.mp3"
at okhttp3.Headers.checkValue(Headers.java:272)
at okhttp3.Headers.of(Headers.java:224)
at okhttp3.MultipartBody$Part.createFormData(MultipartBody.java:259)
at common.myh.com.common.network.RequestBodyUtils.prepareFilePartForCall(RequestBodyUtils.java:69)
at com.geotmt.collection.net.ApiRequest.getMulPartFileListForCall(ApiRequest.java:909)
at com.geotmt.collection.net.ApiRequest.addPhoneRecord(ApiRequest.java:865)
原因分析
如图报错处
createFormData方法源码:
public static Part createFormData(String name, @Nullable String filename, RequestBody body) {
if (name == null) {
throw new NullPointerException("name == null");
}
StringBuilder disposition = new StringBuilder("form-data; name=");
appendQuotedString(disposition, name);
if (filename != null) {
disposition.append("; filename=");
appendQuotedString(disposition, filename);
}
/**
* 最后掉用 Headers.of方法
*/
return create(Headers.of("Content-Disposition", disposition.toString()), body);
}
继续看Headers的of方法源码
public static Headers of(String... namesAndValues) {
if (namesAndValues == null) throw new NullPointerException("namesAndValues == null");
if (namesAndValues.length % 2 != 0) {
throw new IllegalArgumentException("Expected alternating header names and values");
}
// Make a defensive copy and clean it up.
namesAndValues = namesAndValues.clone();
for (int i = 0; i < namesAndValues.length; i++) {
if (namesAndValues[i] == null) throw new IllegalArgumentException("Headers cannot be null");
namesAndValues[i] = namesAndValues[i].trim();
}
// Check for malformed headers.
for (int i = 0; i < namesAndValues.length; i += 2) {
String name = namesAndValues[i];
String value = namesAndValues[i + 1];
//检查 name 注意抛出的异常是此处抛出的
checkName(name);
//检查 value
checkValue(value, name);
}
return new Headers(namesAndValues);
}
chekName()方法源码校验文件的名字
static void checkName(String name) {
if (name == null) throw new NullPointerException("name == null");
if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
for (int i = 0, length = name.length(); i < length; i++) {
char c = name.charAt(i);
if (c <= '\u0020' || c >= '\u007f') {
/**
* 最终抛出的异常的地方
*/
throw new IllegalArgumentException(Util.format(
"Unexpected char %#04x at %d in header name: %s", (int) c, i, name));
}
}
}
解决办法
方案一不完美,直接try catch
这种方案本质上没解决问题, 不是一个好的方案
方案二 将文件名进行规范,想办法去掉中文
有很多办法如通过 文件名的md5值进行或者通过 URLEncoder进行编码, 到了服务器在进行解码等
URLEncoder.encode(file.getName(), "UTF-8")