如何解决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

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

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

<Context allowCasualMultipartParsing=
"true"

...

</Context>

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

}

}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容