基于HttpClient上传文件中文名乱码的解决

网友投稿 892 2023-01-02

基于HttpClient上传文件中文名乱码的解决

基于HttpClient上传文件中文名乱码的解决

现象

使用HttpClient工具上传文件时,如果文件名是中文,文件名会乱码

文件名乱码的代码

private HttpEntity buildEntity(Long scenarioId, List 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上传文件时,会调用doWriteTo方法,写一个输出流,但是在调用formatMultipartHeader方法时,底层主要有3种不同的实现,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种不同的实现,不同的mode,实现方式不一样,采用的字符集也不同

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方法,不同的模式有不同的实现方式

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 paramsList = new ArrayList(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 bodyPartsCopy = bodyParts != null ? new ArrayList(bodyParts) :

Collections.emptyList();

//此处将mode赋值给modeCopy

final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;

final AbstractMultipartForm form;

//此处根据modeCopy的值不同,构造3种form,每种的字符集都不一样,也是产生乱码的根源

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 parts;

public HttpBrowserCompatibleMultipart(

final Charset charset,

final String boundary,

final List parts) {

super(charset, boundary);

this.parts = parts;

}

@Override

public List 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 parts;

public HttpRFC6532Multipart(

final Charset charset,

final String boundary,

final List parts) {

super(charset, boundary);

this.parts = parts;

}

@Override

public List 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 parts;

public HttpStrictMultipart(

final Charset charset,

final String boundary,

final List parts) {

super(charset, boundary);

this.parts = parts;

}

@Override

public List 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;

}

解决方法

知道乱码产生的根源,乱码问题也就好解决了,解决方式有两种

设置mode为:BROWSER_COMPATIBLE,并设置字符集为UTF8

private HttpEntity buildEntity(Long scenarioId, List 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 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();

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:vue小程序组件拖拽开发(vue可拖动组件)
下一篇:MultipartFile文件判断是否存在的操作
相关文章

 发表评论

暂时没有评论,来抢沙发吧~