4.1 QP编码

QP编码quoted-printable)是邮件协议中的一种内容编码方式,Quoted-printable是使用可打印的ASCII字符(如字母、数字与“=”)表示各种编码格式下的字符,以便能在7-bit数据通路上传输8-bit数据, 或者更一般地说在非8-bit clean媒体上正确处理数据,这被定义为MIME content transfer encoding
示例 - JavaQP编码代码:

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ page import="javax.mail.internet.MimeUtility" %>
  3. <%
  4. String qp = request.getParameter("qp");
  5. String encode = MimeUtility.encodeWord(qp);
  6. String decode = MimeUtility.decodeWord(encode);
  7. out.println("<pre>\nQP-Encoding: " + encode + "\nQP-Decode: " + decode);
  8. %>

字符串:测试.jsp编码后的结果如下:
5. 4. 文件上传 - 编码特性 - 图1QP编码本与文件上传没有什么关系,但是由于在Java中最常用的Apache commons fileupload库从1.3开始支持了RFC 2047 Header值编码,从而支持解析使用QP编码后的文件名。
上传文件的时候选一个文件名经过QP编码后的文件,如:=?UTF-8?Q?=E6=B5=8B=E8=AF=95=2Ejsp?=(测试.jsp)。
示例 - 文件上传测试:
5. 4. 文件上传 - 编码特性 - 图2
示例 - Payload:

  1. Content-Disposition: form-data; name="file"; filename="=?UTF-8?Q?=E6=B5=8B=E8=AF=95=2Ejsp?="

编码处理类:org.apache.commons.fileupload.util.mime.MimeUtility#decodeText
`<br />![](https://cdn.nlark.com/yuque/0/2021/png/90007/1614135812554-976f7bbd-806e-453a-956f-206c4f07e5c8.png#align=left&display=inline&height=898&margin=%5Bobject%20Object%5D&originHeight=898&originWidth=2456&size=0&status=done&style=none&width=2456)<br />文件上传成功后文件名被编码成了测试.jsp。<br />Spring MVC中同样支持QP编码,在Spring中有两种处理MultipartResolverorg.springframework.web.multipart.commons.CommonsMultipartResolverorg.springframework.web.multipart.support.StandardServletMultipartResolverCommonsMultipartResolver使用的是commons fileupload解析的所以支持QP编码。StandardMultipartHttpServletRequest比较特殊,Spring 4没有处理QP编码:<br />![](https://cdn.nlark.com/yuque/0/2021/png/90007/1614135812523-6eca20ae-d636-4ef2-8e5e-a517da75ea00.png#align=left&display=inline&height=1124&margin=%5Bobject%20Object%5D&originHeight=1124&originWidth=2076&size=0&status=done&style=none&width=2076)<br />但是在Spring 5修改了实现,如果文件名是=?开始?=结尾的话会调用javax.mail库的MimeDelegate`解析QP编码:
5. 4. 文件上传 - 编码特性 - 图3

javax.mail库不是JDK自带的,必须自行引包,如果不存在该包也将无法解析,SpringBoot + Spring4默认使用的是StandardServletMultipartResolver,但是基于配置的Spring MVC中经常会使用CommonsMultipartResolver,如:

  1. <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  2. <property name="defaultEncoding" value="UTF-8"></property>
  3. <property name="maxUploadSize" value="50000000"></property>
  4. <property name="maxInMemorySize" value="1024"></property>
  5. </bean>

4.2 Spring 内置文件名编码特性

Spring会对文件上传的名称做特殊的处理,org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#parseRequest内置了一种比较特殊的解析文件名的方式,如果传入的multipart请求无法直接使用filename=解析出文件名,Spring还会使用content-disposition解析一次(使用filename*=解析文件名)。
在文件上传时,修改Content-Disposition中的filename=filename*="UTF-8'1.jpg'1.jsp"
5. 4. 文件上传 - 编码特性 - 图4
Spring4的org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#parseRequest解析逻辑:
5. 4. 文件上传 - 编码特性 - 图5
Spring4的org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#extractFilenameWithCharset代码如下:
5. 4. 文件上传 - 编码特性 - 图6
extractFilenameWithCharset支持对传入的文件名编码,示例中传入的UTF-8'1.jpg'1.jsp会被解析成UTF-8编码,最终的文件名为1.jsp,而1.jpg则会被丢弃。
Spring5的org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#parseRequest除了支持QP编码以外,优化了Spring4的解析文件名的方式:
5. 4. 文件上传 - 编码特性 - 图7
org.springframework.http.ContentDisposition#parse代码:
5. 4. 文件上传 - 编码特性 - 图8
文件上传成功:
5. 4. 文件上传 - 编码特性 - 图9
示例 - Payload:

  1. Content-Disposition: form-data; name="file"; filename*="1.jsp"
  2. Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'1.jsp"
  3. Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'=?UTF-8?Q?=E6=B5=8B=E8=AF=95=2Ejsp?="