如何解决SpringMVC文件上传问题

解决方案一: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

"filterMultipartResolver"
class=
"org.springframework.web.multipart.commons.CommonsMultipartResolver"

"maxUploadSize"
value=
"100000000"
/>

"defaultEncoding"
value=
"utf-8"
/>

默认情况下,root spring application context 是applicationContext.xml。*也可以在web.xml中进行配置

*web.xml

1
2
3
4
5
6
7

contextConfigLocation

classpath*:springWebMultipartContext.xml

  1. 为tomcat添加multipart 支持
    Tomcat7.0+ 已经内置了multipart支持,但是必须显示激活。修改全局tomcat配置文件context.xml,或者为war的本地context.xml.
    1
    2
    3
    4

"true"

...

  1. 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




然后执行数据绑定:
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> multipartFiles, MutablePropertyValues mpvs) {

for
(Map.Entry> entry : multipartFiles.entrySet()) {

String key = entry.getKey();

List 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> 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);

}

}

  1. 如果大部分接口还是想使用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 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

你可能感兴趣的:(如何解决SpringMVC文件上传问题)