springMVC,文件上传超过大小限制时的异常处理

​ 今天要做一个文件上传的功能。想限制上传文件的大小,但是同时又想当用户上传文件的时候给出友好的页面提示所上传的文件过大。springMVC出现异常的时候默认的是出现一个网站异常的界面。那如何手动处理springMVC抛出的异常,并根据对应异常跳转到我们指定的页面呢?

​ 谷歌了一下。可配置的大体有以下3种解决办法。

一、在spring-mvc.xml 中配置

​ 在spring-mvc.xml中加上如下配置。

1
2
3
4
5
6
7
8
9
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">redirect:/login/error</prop>
<prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">/error</prop>
</props>
</property>
<property name="defaultStatusCode" value="500"/>
</bean>

这个配置可以指定哪种异常用哪种策略。比如上面我们配置了抛出MaxUploadSizeExceededException的时候跳转到/error这个视图。

二、实现HandlerExceptionResolver接口

​ 对应的controller类实现HandlerExceptionResolver的resolveException方法,可以在代码中根据不同的异常来执行对应的策略。resolveException代码如下(返回json格式数据):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
ModelAndView view = new ModelAndView();
view.setView(new MappingJacksonJsonView());
if (ex instanceof MaxUploadSizeExceededException) {
System.out.println("size excetion");
view.addObject("error data");
return view;
} else {
System.out.println("normal exception");
}
return null;
}

三、用注解@ControllerAdvice 实现全局异常捕获并处理

​ 上面那种方法只能处理单个controller里面的异常。如果想要处理整个应用的异常,可以专门写一个GlobalExceptionHandler类来处理所以的异常。

​ 代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public String handleMaxUploadException(MaxUploadSizeExceededException e, HttpServletRequest request, HttpServletResponse response) {
ModelAndView mav = new ModelAndView();
boolean isJson = request.getRequestURL().toString().contains(".json");
if (isJson) {
mav.setView(new MappingJacksonJsonView());
mav.addObject("result", "nok");
} else {
mav.setViewName("error");
}
return "error";
}
}

如果试了以上3中办法可以了,就不用往下看了。


在谷歌上找来找去,基本都是以上几种办法。看其他人好像都没有什么问题,但是我把上面3种方法来回折腾了好几遍都死活不行(奇怪的是chrome、ie不可以,火狐浏览器却可以….其他浏览器没试),也不懂问题出在哪里,只能以后有空把springMVC的源码翻一遍看看了。最后还是用拦截器解决了这个问题。

四、拦截器拦截附件大小超过限制的请求

​ 只能自己写拦截器做附件大小限制。先不设置spring-mvc.xml中CommonsMultipartResolver的fileMaxSize的大小。

1
2
3
4
5
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--表示延迟解析这个文件,先判断文件大小后再解析文件 -->
<property name="resolveLazily" value="true"/>
</bean>

设置自己的拦截器

1
2
3
4
5
6
7
8
9
10
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/"/>
<mvc:mapping path="/*"/>
<mvc:mapping path="/**"/>
<bean class="com.imp.interceptor.FileSizeInterceptor">
<property name="maxFileSize" value="1048576"/>
</bean>
</mvc:interceptor>
</mvc:interceptors>

拦截器代码

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
31
32
33
34
35
36
37
38
39
/**
* Created by yangjb on 2017/3/20.
* 拦截附件大小超过maxFileSize的请求
*/
public class FileSizeInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(FileSizeInterceptor.class);
//B为单位
private long maxFileSize;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean isFileForm = ServletFileUpload.isMultipartContent(request);
if (isFileForm) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile file = multipartRequest.getFile("file");
CommonsMultipartFile cf = (CommonsMultipartFile) file;
if (cf.getSize() > maxFileSize) {
LOGGER.info("file {} size {}B is bigger than {}B", cf.getSize(), cf.getOriginalFilename(), maxFileSize);
response.sendRedirect(request.getContextPath() + "/process/list");
return false;
}
}
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
public long getMaxFileSize() {
return maxFileSize;
}
public void setMaxFileSize(long maxFileSize) {
this.maxFileSize = maxFileSize;
}
}

写完终于搞定这个问题。

总结:

  • 上面的拦截器是针对所有url进行拦截,并且只获取name=file的文件进行大小判断。可以进行相应改进
  • 网上有些人在前端做限制,个人认为不够安全。最好能前后台都做限制。前端做限制的代码请自行谷歌。

@yangjb 2017.03.20