基于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的值不同,構(gòu)造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); //可以看到此處的字符集采用的是設(shè)置的字符集 writeField(cd, this.charset, out); final String filename = part.getBody().getFilename(); if (filename != null) { final MinimalField ct = header.getField(MIME.CONTENT_TYPE); //可以看到此處的字符集采用的也是設(shè)置的字符集 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)生的根源,亂碼問題也就好解決了,解決方式有兩種
設(shè)置mode為:BROWSER_COMPATIBLE,并設(shè)置字符集為UTF8
private HttpEntity buildEntity(Long scenarioId, List<String> groupIds, String extension, File fileToUpload) { MultipartEntityBuilder builder = MultipartEntityBuilder.create(); //設(shè)置模式為BROWSER_COMPATIBLE,并設(shè)置字符集為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(); }
設(shè)置模式為:RFC6532
private HttpEntity buildEntity(Long scenarioId, List<String> groupIds, String extension, File fileToUpload) { MultipartEntityBuilder builder = MultipartEntityBuilder.create(); //設(shè)置模式為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表單操作應(yīng)用及注意事項
- HttpClient實現(xiàn)表單提交上傳文件
相關(guān)文章
SpringBoot MainApplication類文件的位置詳解
這篇文章主要介紹了SpringBoot MainApplication類文件的位置詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01關(guān)于Java中如何實現(xiàn)文件的讀寫操作
在Java中,可以使用File和FileInputStream、FileOutputStream、BufferedReader、PrintWriter等類來進行文件讀寫操作,需要的朋友可以參考下2023-05-05java逗號分隔String字符串及數(shù)組、集合相互轉(zhuǎn)換
我們在日常開發(fā)時會經(jīng)常遇到將一個字符串按照逗號進行分割,這篇文章主要給大家介紹了關(guān)于java逗號分隔String字符串及數(shù)組、集合相互轉(zhuǎn)換的相關(guān)資料,文中給出了詳細的代碼示例,需要的朋友可以參考下2024-02-02Java實現(xiàn)PDF轉(zhuǎn)Word的示例代碼(無水印無頁數(shù)限制)
這篇文章主要為大家詳細介紹了如何利用Java語言實現(xiàn)PDF轉(zhuǎn)Word文件的效果,并可以無水印、無頁數(shù)限制。文中的示例代碼講解詳細,需要的可以參考一下2022-05-05Java如何利用LocalDate獲取某個月的第一天與最后一天日期
這篇文章主要給大家介紹了關(guān)于Java如何利用LocalDate獲取某個月的第一天與最后一天日期的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-01-01