解决方案一:CommonsMultipartResolver
说明:该方案能够适用于servlet-3.0之前版本。
步骤:
使用Apache Commons Multipart Resolver
, 在spring的root application context 下定义类型为CommonsMultipartResolver的bean,该bean的名字一定要取*filterMultipartResolver *。在此强调一下,一定要在root spring application context下定义,名字也不能更改。
applicationContext.xml1
2
3
4
5
<bean id=
"filterMultipartResolver"
class=
"org.springframework.web.multipart.commons.CommonsMultipartResolver"
<property name=
"maxUploadSize"
value=
"100000000"
/>
<property name=
"defaultEncoding"
value=
"utf-8"
/>
</bean>
默认情况下,root spring application context 是applicationContext.xml。*也可以在web.xml中进行配置
*web.xml
1
2
3
4
5
6
7
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:springWebMultipartContext.xml
</param-value>
</context-param>
- 为tomcat添加multipart 支持
Tomcat7.0+ 已经内置了multipart支持,但是必须显示激活。修改全局tomcat配置文件context.xml,或者为war的本地context.xml.
1
2
3
4
<Context allowCasualMultipartParsing=
"true"
...
</Context>
- 在web.xml添加*MultipartFilter *
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<
filter
<
description
Allows the application to accept multipart file data.
</
description
<
display-name
springMultipartFilter</
display-name
<
filter-name
springMultipartFilter</
filter-name
<
filter-class
org.springframework.web.multipart.support.MultipartFilter</
filter-class
</
filter
<
filter-mapping
<
filter-name
springMultipartFilter</
filter-name
<
url-pattern
/*</
url-pattern
</
filter-mapping
在配置完以上步骤以后,可以使用dataBinding的方式,访问文件。
1
public
String index(HttpServletRequest request,
@RequestParam
(
"fileName"
)MultipartFile file)
源码分析:
从上面的配置可以看出,文件上传是通过Servlet-api的Filter来实现的,Filter执行时机此处不再分析。MultipartFilter会在DispatchServlet执行之前,对Request进行一次处理。
org.springframework.web.multipart.support.MultipartFilter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected
void
doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws
ServletException, IOException {
MultipartResolver multipartResolver = lookupMultipartResolver(request);
HttpServletRequest processedRequest = request;
if
(multipartResolver.isMultipart(processedRequest)) {
if
(logger.isDebugEnabled()) {
logger.debug(
"Resolving multipart request ["
- processedRequest.getRequestURI() +
"] with MultipartFilter"
);
}
**
**
**processedRequest = multipartResolver.resolveMultipart(processedRequest);
**
}
else
{
// A regular request...
if
(logger.isDebugEnabled()) {
logger.debug(
"Request ["
- processedRequest.getRequestURI() +
"] is not a multipart request"
);
}
}
try
{
filterChain.doFilter(processedRequest, response);
}
finally
{
if
(processedRequest
instanceof
MultipartHttpServletRequest) {
multipartResolver.cleanupMultipart((MultipartHttpServletRequest) processedRequest);
}
}
}
org.springframework.web.multipart.commons.CommonsMultipartResolver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public
MultipartHttpServletRequest resolveMultipart(
final
HttpServletRequest request)
throws
MultipartException {
Assert.notNull(request,
"Request must not be null"
);
if
(
this
.resolveLazily) {
return
new
DefaultMultipartHttpServletRequest(request) {
@Override
protected
void
initializeMultipart() {
MultipartParsingResult parsingResult = parseRequest(request);
setMultipartFiles(parsingResult.getMultipartFiles());
setMultipartParameters(parsingResult.getMultipartParameters());
setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
}
};
}
else
{
**MultipartParsingResult parsingResult = parseRequest(request);
**
return
new
DefaultMultipartHttpServletRequest(request,
***parsingResult.getMultipartFiles()
***,
parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
}
}
以上两个方法的作用就是通过解析request,把multiparts解析为以文件名为Key,以MultipartFile为value的Map。Http数据传输的细节就不在列出。此处需要注意的是该Map是***MultiValueMap<String, MultipartFile>、
然后执行数据绑定:
org.springframework.web.bind.WebDataBinder.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected
void
bindMultipart(Map<String, List<MultipartFile>> multipartFiles, MutablePropertyValues mpvs) {
for
(Map.Entry<String, List<MultipartFile>> entry : multipartFiles.entrySet()) {
String key = entry.getKey();
List<MultipartFile> values = entry.getValue();
if
(values.size() ==
1
) {
MultipartFile value = values.get(
0
);
if
(isBindEmptyMultipartFiles() || !value.isEmpty()) {
mpvs.add(key, value);
}
}
else
{
mpvs.add(key, values);
}
}
}
因此在SpringMVC的controller中可以通过
@RequestParam
(
"fileName"
)MultipartFile file相应的file。
**问题:如果事先不知道文件的名字怎么办呢,即没办法通过数据绑定的方式来获取文件。
**
现在有个比较low的解决办法:
通过以上的步骤,Request中的文件数据流已经被处理成Map<String, List<MultipartFile>> multipartFiles.解决方法:
1.如果所有的接口都不使用数据绑定的形式获取文件,此时就不要按照以上步骤来实现。而是直接使用CommonsMultipartResolver来解析Request。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
...Controller...{
//创建一个通用的多部分解析器.
CommonsMultipartResolver commonsMultipartResolver =
new
CommonsMultipartResolver(request.getSession().getServletContext());
//设置编码
commonsMultipartResolver.setDefaultEncoding(”utf-
8
″);
//判断 request 是否有文件上传,即多部分请求...
if
(commonsMultipartResolver.isMultipart(request))
{
//转换成多部分request
MultipartHttpServletRequest multipartRequest =
commonsMultipartResolver.resolveMultipart(request);
// file 是指 文件上传标签的 name=值
// 根据 name 获取上传的文件...
MultipartFile file = multipartRequest.getFile(
"file"
);
//上传后记录的文件...
File imageFile =
new
File(
"fileName"
);
//上传...
file.transferTo(imageFile);
}
}
- 如果大部分接口还是想使用Filter的形式来提供数据绑定。那么还有一个办法就是保证该MultipartFilter最后一个执行,此时Request的类型是 instanceof MultipartHttpServletRequest。可以通过这个提示来解决问题。
1
2
3
4
5
6
7
8
9
10
11
@RequestMapping
(method = RequestMethod.POST)
public
String index(HttpServletRequest request)
{
List<MultipartFile> files =
null
;
if
(request
instanceof
MultipartHttpServletRequest) {
files = getUploadFiles((MultipartHttpServletRequest)request);
parameter = getJsonPara((MultipartHttpServletRequest)request, UserProfile.
class
);
}
else
{
parameter = getJsonPara(request, UserProfile.
class
);
}
可以在web.xml中配置filter的执行顺序:1
2
3
4
5
6
7
<
absolute-ordering
<
name
</
name
<
name
</
name
<
name
</
name
<
name
</
name
<
name
springMultipartFilter</
name
</
absolute-ordering
参考:
[1] http://stackoverflow.com/questions/21397939/spring-security-3-2-csrf-support-for-multipart-requests
[2] http://stackoverflow.com/questions/23223762/multipartconfig-with-servlet-3-0-on-spring-mvc
[3] https://jira.spring.io/browse/SPR-11373
[4]http://stackoverflow.com/questions/6560969/how-to-define-servlet-filter-order-of-execution-using-annotations-in-war
[5] http://www.cnblogs.com/Fskjb/archive/2010/03/27/1698448.html
[6]http://stackoverflow.com/questions/10527837/spring-3-0-multipartfile-upload
[7]http://yanglei008.iteye.com/blog/246920#
[8]http://stackoverflow.com/questions/26118099/how-to-config-commonsmultipartresolver-in-spring4-without-xml-to-upload-file