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

现象

使用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上传文件时,会调用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<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;
        //此处根据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<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;
}

解决方法

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

设置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();
    }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java中文传值乱码问题的解决方法

    本文实例为大家分享了java中文传值乱码问题,以及解决方法,供大家参考,具体内容如下 一般编码格式设置: 1.可以经过两次编码处理,即设置字符集后,在插入前解码字符集,也是最有效的方式 设置字符集: String value=null; try { value= URLEncoder.encode(jsonObjectPar.getString("value"), "UTF-8"); } catch (UnsupportedEncodingException e)

  • Java中FTPClient上传中文目录、中文文件名乱码问题解决方法

    问题描述: 使用org.apache.commons.net.ftp.FTPClient创建中文目录.上传中文文件名时,目录名及文件名中的中文显示为"??". 原因: FTP协议里面,规定文件名编码为iso-8859-1,所以目录名或文件名需要转码. 解决方案: 1.将中文的目录或文件名转为iso-8859-1编码的字符.参考代码: 复制代码 代码如下: String name="目录名或文件名"; name=new String(name.getBytes(&qu

  • 解决使用httpclient传递json数据乱码的问题

    今天用httpclient传输json数据,服务端接受数据 中文乱码,下面分别贴上修改前与修改后的代码以及原因分析 (1)修改前: client端 public String sendHttpPost(String httpUrl, String data) { // 创建post请求 HttpPost httpPost = new HttpPost(httpUrl); StringEntity entity; try { entity = new StringEntity(data); ent

  • Java下载文件时文件名乱码问题解决办法

    复制代码 代码如下: public static String toUtf8String(String s) { StringBuffer sb = new StringBuffer();              for (int i = 0; i < s.length(); i++) {                  char c = s.charAt(i);                  if (c >= 0 && c <= 255) {          

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

    现象 使用HttpClient工具上传文件时,如果文件名是中文,文件名会乱码 文件名乱码的代码: private HttpEntity buildEntity(Long scenarioId, List<String> groupIds, String extension,File fileToUpload) { MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addTextBody("

  • C# 使用HttpClient上传文件并附带其他参数的步骤

    HttpClient和MultipartFormDataContent(传送门)最低适用于.NET Framework 4.5版本 发送端代码 using (HttpClient client = new HttpClient()) { var content = new MultipartFormDataContent(); //添加字符串参数,参数名为qq content.Add(new StringContent("123456"), "qq"); strin

  • spring boot上传文件出错问题如何解决

    这篇文章主要介绍了spring boot上传文件出错问题如何解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location

  • Apache上传文件500错误的解决方法

    打开Apache的httpd.conf配置文件,在这个文件里增加下面的参数设置即可. 复制代码 代码如下: MaxRequestLen 10240000 我这里设置为10M,比PHP上传默认最大值8M略大即可.你可以根据自己的需要,调整这个参数的值,注意这个参数的单位是字节. 配置完成后重启Apache即可.

  • php上传文件中文文件名乱码的解决方法

    可能会有不少朋友碰到一些问题就是上传文件时如果是英文倒好原文名不会有问题,如果是中文可能就会出现乱码了,今天我来给大家总结一下导致乱码php上传文件中文文件名乱码的原因与解决办法吧. 这几天在windows下安装了XAMPP,准备初步学习一下php的相关内容.这几天接触到了php上传文件,但是出现了一个郁闷问题,我准备上传一个excel文件,但是如果文件名是中文名就会报错. 一来二去很是郁闷,后来仔细想了想应该是文件编码的问题,我写的php文件使用的是UTF-8编码,如果没有猜错APACHE处理

  • C# HttpClient Post参数同时上传文件的实现

    目录 HttpClient Post参数同时上传文件 Demo 如下 HttpClient上传文件到服务器(multipart/form-data) HttpClient Post参数同时上传文件 Demo 如下 using (var client = new HttpClient()) {     using (var multipartFormDataContent = new MultipartFormDataContent())     {         var values = ne

  • Android程序开发通过HttpURLConnection上传文件到服务器

    一:实现原理 最近在做Android客户端的应用开发,涉及到要把图片上传到后台服务器中,自己选择了做Spring3 MVC HTTP API作为后台上传接口,android客户端我选择用HttpURLConnection来通过form提交文件数据实现上传功能,本来想网上搜搜拷贝一下改改代码就好啦,发现根本没有现成的例子,多数的例子都是基于HttpClient的或者是基于Base64编码以后作为字符串来传输图像数据,于是我不得不自己动手,参考了网上一些资料,最终实现基于HttpURLConnect

  • 基于SpringBoot上传任意文件功能的实现

    一.pom文件依赖的添加 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</gr

  • 基于jQuery Ajax实现上传文件

    本文实例为大家分享了基于jQuery Ajax实现上传文件的关键代码,供大家参考,具体内容如下 JS代码: //保存 function btnAdd() { var formData = new FormData($("#frm")[0]); $.ajax({ url: "/Admin/ContentManage/SaveEdit", type: "POST", data: formData, contentType: false, //必须fa

  • HttpClient通过Post上传文件的实例代码

    在之前一段的项目中,使用Java模仿Http Post方式发送参数以及文件,单纯的传递参数或者文件可以使用URLConnection进行相应的处理. 但是项目中涉及到既要传递普通参数,也要传递多个文件(不是单纯的传递XML文件).在网上寻找之后,发现是使用HttClient来进行响应的操作,起初尝试多次依然不能传递参数和传递文件,后来发现时因为当使用HttpClient时,不能使用request.getParameter()对普通参数进行获取,而要在服务器端使用Upload来进行操作. Http

随机推荐