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 />![](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中有两种处理
Multipart的
Resolver:
org.springframework.web.multipart.commons.CommonsMultipartResolver和
org.springframework.web.multipart.support.StandardServletMultipartResolver。
CommonsMultipartResolver使用的是
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编码:
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?="