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")