4.1 QP编码
QP编码( quoted-printable)是邮件协议中的一种内容编码方式,Quoted-printable是使用可打印的ASCII字符(如字母、数字与“=”)表示各种编码格式下的字符,以便能在7-bit数据通路上传输8-bit数据, 或者更一般地说在非8-bit clean媒体上正确处理数据,这被定义为MIME content transfer encoding。
示例 - JavaQP编码代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="javax.mail.internet.MimeUtility" %><%String qp = request.getParameter("qp");String encode = MimeUtility.encodeWord(qp);String decode = MimeUtility.decodeWord(encode);out.println("<pre>\nQP-Encoding: " + encode + "\nQP-Decode: " + decode);%>
字符串:测试.jsp编码后的结果如下:
QP编码本与文件上传没有什么关系,但是由于在Java中最常用的Apache commons fileupload库从1.3开始支持了RFC 2047 Header值编码,从而支持解析使用QP编码后的文件名。
上传文件的时候选一个文件名经过QP编码后的文件,如:=?UTF-8?Q?=E6=B5=8B=E8=AF=95=2Ejsp?=(测试.jsp)。
示例 - 文件上传测试:
示例 - Payload:
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 /><br />文件上传成功后文件名被编码成了测试.jsp。<br />Spring MVC中同样支持QP编码,在Spring中有两种处理Multipart的Resolver:org.springframework.web.multipart.commons.CommonsMultipartResolver和org.springframework.web.multipart.support.StandardServletMultipartResolver。CommonsMultipartResolver使用的是commons fileupload解析的所以支持QP编码。StandardMultipartHttpServletRequest比较特殊,Spring 4没有处理QP编码:<br /><br />但是在Spring 5修改了实现,如果文件名是=?开始?=结尾的话会调用javax.mail库的MimeDelegate`解析QP编码:
javax.mail库不是JDK自带的,必须自行引包,如果不存在该包也将无法解析,SpringBoot + Spring4默认使用的是StandardServletMultipartResolver,但是基于配置的Spring MVC中经常会使用CommonsMultipartResolver,如:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><property name="defaultEncoding" value="UTF-8"></property><property name="maxUploadSize" value="50000000"></property><property name="maxInMemorySize" value="1024"></property></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":
Spring4的org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#parseRequest解析逻辑:
Spring4的org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#extractFilenameWithCharset代码如下:
extractFilenameWithCharset支持对传入的文件名编码,示例中传入的UTF-8'1.jpg'1.jsp会被解析成UTF-8编码,最终的文件名为1.jsp,而1.jpg则会被丢弃。
Spring5的org.springframework.web.multipart.support.StandardMultipartHttpServletRequest#parseRequest除了支持QP编码以外,优化了Spring4的解析文件名的方式:
org.springframework.http.ContentDisposition#parse代码:
文件上传成功:
示例 - Payload:
Content-Disposition: form-data; name="file"; filename*="1.jsp"Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'1.jsp"Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'=?UTF-8?Q?=E6=B5=8B=E8=AF=95=2Ejsp?="
