基于HttpClient上傳文件中文名亂碼的解決
現(xiàn)象
使用HttpClient工具上傳文件時,如果文件名是中文,文件名會亂碼
文件名亂碼的代碼:
private HttpEntity buildEntity(Long scenarioId, List<String> groupIds, String extension,File fileToUpload) {
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("scenarioId", scenarioId.toString());
for (String groupId : groupIds) {
builder.addTextBody("groupIds", groupId);
}
builder.addTextBody("extension", extension);
builder.addPart("fileToUpload", new FileBody(fileToUpload));
builder.addTextBody("type", AssetFileTypeEnum.CSV.getName());
builder.addTextBody("isSplit", "false");
builder.addTextBody("isRefresh", "false");
return builder.build();
亂碼原因:
HttpClient上傳文件時,會調(diào)用doWriteTo方法,寫一個輸出流,但是在調(diào)用formatMultipartHeader方法時,底層主要有3種不同的實現(xiàn),3種方式的采用的字符集不一樣
HttpClient中的doWriteTo方法:
void doWriteTo(
final OutputStream out,
final boolean writeContent) throws IOException {
final ByteArrayBuffer boundaryEncoded = encode(this.charset, this.boundary);
for (final FormBodyPart part: getBodyParts()) {
writeBytes(TWO_DASHES, out);
writeBytes(boundaryEncoded, out);
writeBytes(CR_LF, out);
//此處代碼主要有3種不同的實現(xiàn),不同的mode,實現(xiàn)方式不一樣,采用的字符集也不同
formatMultipartHeader(part, out);
writeBytes(CR_LF, out);
if (writeContent) {
part.getBody().writeTo(out);
}
writeBytes(CR_LF, out);
}
writeBytes(TWO_DASHES, out);
writeBytes(boundaryEncoded, out);
writeBytes(TWO_DASHES, out);
writeBytes(CR_LF, out);
}
其中的formatMultipartHeader方法,不同的模式有不同的實現(xiàn)方式
MultipartEntityBuilder
MultipartFormEntity buildEntity() {
String boundaryCopy = boundary;
if (boundaryCopy == null && contentType != null) {
boundaryCopy = contentType.getParameter("boundary");
}
if (boundaryCopy == null) {
boundaryCopy = generateBoundary();
}
Charset charsetCopy = charset;
if (charsetCopy == null && contentType != null) {
charsetCopy = contentType.getCharset();
}
final List<NameValuePair> paramsList = new ArrayList<NameValuePair>(2);
paramsList.add(new BasicNameValuePair("boundary", boundaryCopy));
if (charsetCopy != null) {
paramsList.add(new BasicNameValuePair("charset", charsetCopy.name()));
}
final NameValuePair[] params = paramsList.toArray(new NameValuePair[paramsList.size()]);
final ContentType contentTypeCopy = contentType != null ?
contentType.withParameters(params) :
ContentType.create("multipart/" + DEFAULT_SUBTYPE, params);
final List<FormBodyPart> bodyPartsCopy = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) :
Collections.<FormBodyPart>emptyList();
//此處將mode賦值給modeCopy
final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;
final AbstractMultipartForm form;
//此處根據(jù)modeCopy的值不同,構造3種form,每種的字符集都不一樣,也是產(chǎn)生亂碼的根源
switch (modeCopy) {
case BROWSER_COMPATIBLE:
form = new HttpBrowserCompatibleMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
break;
case RFC6532:
form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
break;
default:
form = new HttpStrictMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
}
return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
}
public HttpEntity build() {
return buildEntity();
}
BROWSER_COMPATIBLE模式中的formatMultipartHeader方法
class HttpBrowserCompatibleMultipart extends AbstractMultipartForm {
private final List<FormBodyPart> parts;
public HttpBrowserCompatibleMultipart(
final Charset charset,
final String boundary,
final List<FormBodyPart> parts) {
super(charset, boundary);
this.parts = parts;
}
@Override
public List<FormBodyPart> getBodyParts() {
return this.parts;
}
/**
* Write the multipart header fields; depends on the style.
*/
@Override
protected void formatMultipartHeader(
final FormBodyPart part,
final OutputStream out) throws IOException {
// For browser-compatible, only write Content-Disposition
// Use content charset
final Header header = part.getHeader();
final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION);
//可以看到此處的字符集采用的是設置的字符集
writeField(cd, this.charset, out);
final String filename = part.getBody().getFilename();
if (filename != null) {
final MinimalField ct = header.getField(MIME.CONTENT_TYPE);
//可以看到此處的字符集采用的也是設置的字符集
writeField(ct, this.charset, out);
}
}
}
RFC6532模式中的formatMultipartHeader方法
class HttpRFC6532Multipart extends AbstractMultipartForm {
private final List<FormBodyPart> parts;
public HttpRFC6532Multipart(
final Charset charset,
final String boundary,
final List<FormBodyPart> parts) {
super(charset, boundary);
this.parts = parts;
}
@Override
public List<FormBodyPart> getBodyParts() {
return this.parts;
}
@Override
protected void formatMultipartHeader(
final FormBodyPart part,
final OutputStream out) throws IOException {
// For RFC6532, we output all fields with UTF-8 encoding.
final Header header = part.getHeader();
for (final MinimalField field: header) {
//可以看到此處的字符集默認采用UTF8
writeField(field, MIME.UTF8_CHARSET, out);
}
}
}
默認模式中的formatMultipartHeader方法
class HttpStrictMultipart extends AbstractMultipartForm {
private final List<FormBodyPart> parts;
public HttpStrictMultipart(
final Charset charset,
final String boundary,
final List<FormBodyPart> parts) {
super(charset, boundary);
this.parts = parts;
}
@Override
public List<FormBodyPart> getBodyParts() {
return this.parts;
}
@Override
protected void formatMultipartHeader(
final FormBodyPart part,
final OutputStream out) throws IOException {
// For strict, we output all fields with MIME-standard encoding.
//從上面注釋中可以看到,此處的字符集采用的是默認字符集即ASCII(下面MIME類中可以看到)
final Header header = part.getHeader();
for (final MinimalField field: header) {
writeField(field, out);
}
}
}
MIME類
public final class MIME {
public static final String CONTENT_TYPE = "Content-Type";
public static final String CONTENT_TRANSFER_ENC = "Content-Transfer-Encoding";
public static final String CONTENT_DISPOSITION = "Content-Disposition";
public static final String ENC_8BIT = "8bit";
public static final String ENC_BINARY = "binary";
/** The default character set to be used, i.e. "US-ASCII" */
public static final Charset DEFAULT_CHARSET = Consts.ASCII;
/** UTF-8 is used for RFC6532 */
public static final Charset UTF8_CHARSET = Consts.UTF_8;
}
解決方法
知道亂碼產(chǎn)生的根源,亂碼問題也就好解決了,解決方式有兩種
設置mode為:BROWSER_COMPATIBLE,并設置字符集為UTF8
private HttpEntity buildEntity(Long scenarioId, List<String> groupIds, String extension,
File fileToUpload) {
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
//設置模式為BROWSER_COMPATIBLE,并設置字符集為UTF8
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.setCharset(Charset.forName("UTF-8"));
builder.addTextBody("scenarioId", scenarioId.toString());
for (String groupId : groupIds) {
builder.addTextBody("groupIds", groupId);
}
builder.addTextBody("extension", extension);
builder.addPart("fileToUpload", new FileBody(fileToUpload));
builder.addTextBody("type", AssetFileTypeEnum.CSV.getName());
builder.addTextBody("isSplit", "false");
builder.addTextBody("isRefresh", "false");
return builder.build();
}
設置模式為:RFC6532
private HttpEntity buildEntity(Long scenarioId, List<String> groupIds, String extension,
File fileToUpload) {
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
//設置模式為RFC6532
builder.setMode(HttpMultipartMode.RFC6532);
builder.addTextBody("scenarioId", scenarioId.toString());
for (String groupId : groupIds) {
builder.addTextBody("groupIds", groupId);
}
builder.addTextBody("extension", extension);
builder.addPart("fileToUpload", new FileBody(fileToUpload));
builder.addTextBody("type", AssetFileTypeEnum.CSV.getName());
builder.addTextBody("isSplit", "false");
builder.addTextBody("isRefresh", "false");
return builder.build();
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- HttpClient實現(xiàn)文件上傳功能
- C# HttpClient Post參數(shù)同時上傳文件的實現(xiàn)
- C# 使用HttpClient上傳文件并附帶其他參數(shù)的步驟
- Android引用開源框架通過AsyncHttpClient實現(xiàn)文件上傳
- 使用HttpClient實現(xiàn)文件的上傳下載方法
- HttpClient通過Post上傳文件的實例代碼
- httpclient模擬post請求json封裝表單數(shù)據(jù)的實現(xiàn)方法
- Java利用HttpClient模擬POST表單操作應用及注意事項
- HttpClient實現(xiàn)表單提交上傳文件
相關文章
SpringBoot MainApplication類文件的位置詳解
這篇文章主要介紹了SpringBoot MainApplication類文件的位置詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
java逗號分隔String字符串及數(shù)組、集合相互轉換
我們在日常開發(fā)時會經(jīng)常遇到將一個字符串按照逗號進行分割,這篇文章主要給大家介紹了關于java逗號分隔String字符串及數(shù)組、集合相互轉換的相關資料,文中給出了詳細的代碼示例,需要的朋友可以參考下2024-02-02
Java實現(xiàn)PDF轉Word的示例代碼(無水印無頁數(shù)限制)
這篇文章主要為大家詳細介紹了如何利用Java語言實現(xiàn)PDF轉Word文件的效果,并可以無水印、無頁數(shù)限制。文中的示例代碼講解詳細,需要的可以參考一下2022-05-05
Java如何利用LocalDate獲取某個月的第一天與最后一天日期
這篇文章主要給大家介紹了關于Java如何利用LocalDate獲取某個月的第一天與最后一天日期的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-01-01

