学编程,来源栈;先学习,再交钱
SpringMVC:文件上传&下载 / 错误处理:@ExceptionHandler & @ControllerAdvice & log & 回滚 & HandlerExceptionResolver

当前系列: Java Web开发 修改

文件上传

前端提醒注意

  • freemarker中没有(也不需要)默认的文件上传spring form表单标签
  • 所以要自己写原生的form元素,注意这个name,后台取值时会用:
    <input type="file" name="icon" />
  • form中不要忘记添加enctype
    <form method="post" enctype="multipart/form-data">

后台取上传的文件,首先要做好准备:

  • maven导入:
    <dependency>
    	<groupId>commons-fileupload</groupId>
    	<artifactId>commons-fileupload</artifactId>
    	<version>1.3.2</version>
    </dependency>
  • springmvc-servlet.xml配置可解析multipart内容:
    <bean id="multipartResolver"
    	class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    	<!-- 最大允许上传大小5MB -->
    	<property name="maxUploadSize" value="5242880" />
    	<property name="defaultEncoding" value="UTF-8" />
    </bean>

然后在controller中:

  • 直接用handle method参数
    public ModelAndView input(MultipartFile icon
  • 然后就可以用icon:
    • 判断文件是否为空 
      if (!icon.isEmpty()) {
    • 获取原文件名(包含扩展名)
      String fileName = icon.getOriginalFilename();
    • (根据文件头)判断文件类型
      System.out.println(icon.getContentType());
    • 写入文件
      icon.transferTo(filePath);

因为存储文件需要文件路径而不是URL路径,通常需要一个映射,获取文件存储路径(绝对路径):

String path = request.getServletContext().getRealPath("/WEB-INF/icons/");

注意:为了后面开发调试时,通过(HTML的)src属性获取文件方便,这里最好修改eclipse中tomcat的server locations为:

其他就涉及到I/O操作等,比如

  • 创建文件对象:
    File filePath = new File(path + fileName);
  • 如果文件目录不存在,创建目录
    if (!filePath.getParentFile().exists()) {
    	filePath.getParentFile().mkdirs();
    }


动态文件输出

复习:生成BufferedImage对象

因为要由url调用,所以首先,还是要有Controller和Handler Method。

但是,这时候我们不能再返回ModelAndView或者String了,可以直接返回BufferedImage对象:

@RequestMapping
public BufferedImage Show()  {
	return image;
}
演示运行效果:找不到view……

说明SpringMVC仍然将这个handler method当做之前一样的东东,采用一样的流程,找template合成view呢!

我们给handler method添加一个@ResponseBody注解

@ResponseBody
public BufferedImage Show()  {
告诉SpringMVC:这个handler method,要再搞什么view resolver之类的玩意儿,直接将其作为响应主体使用。

演示运行效果:找不到Converter……

因为不使用view resolver,就需要Converter把handler method的返回值,转换成可以发送给客户端的格式。

所以,在springmvc-servlet.xml中配置:

<mvc:annotation-driven>
	<mvc:message-converters>
		<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter" />

PS:<mvc:annotation-driven>是默认包含在springmvc-servlet.xml中,为SpringMVC的:

  • 路径匹配(path-matching)、
  • 异步支持(async-support)、
  • handler method的参数解析(argument-resolver)、
  • 返回值处理(return-value-handlers)和
  • 消息转换(message-converters)

配置Bean的。一般不用更改……

演示:

  • 直接使用url访问http://localhost:8080/17bang/captcha,能够看到图片。
  • 这种图片一样可以通过HTML标签调用:
    <img alt="" src="/17bang/captcha">

最后,BufferedImageHttpMessageConverter默认将image转换成png格式。我们还可以通过注解:

@RequestMapping(produces = MediaType.IMAGE_JPEG_VALUE )
将其转换成其他格式(以上是.jpg格式)


错误处理

大部分的异常我们都可以“不管”,直接往上抛,最后由应用统一处理:

@ExceptionHandler

在Controller中声明一个方法,添加该注解,所有该Controller中抛出的未处理异常都会被该方法处理

@ExceptionHandler({ 
	IllegalArgumentException.class, 
	NullPointerException.class })
public String error(HttpServletResponse response) {
	response.addHeader("level", "error");
	return URLMapping.Error._500;
}

说明:

  • 注解属性可指定可捕获的异常种类
  • 方法一样可以声明各种参数,由SpringMVC框架负责赋值,这里声明了一个response对象
    浏览器F12演示:header中添加了键值对
  • 方法返回同普通handler method,这里重定向到exception.html页面
    public static class Error{
    	public final static String _500 = Redirect +"/exception.html";
    @想一想@:500前面为什么要加_(下划线)?

PS:要想能够访问静态页面,需要在springmvc-servlet.xml中添加:

<mvc:resources mapping="/images/**"location="/images/"/>

如果@ExceptionHandler不指定异常属性,还可以在方法参数中指定,比如:

@ExceptionHandler
public String error(MessagingException me, HttpServletResponse response) {
	System.out.println(me.getMessage());
这样还能获得一个额外的好处:可以拿到exception对象,调用其属性方法等……

@ControllerAdvice

额外声明一个类,给予该注解,可以全局的处理所有Controller中的未处理异常:

@ControllerAdvice
public class ErrorHandle {
	@ExceptionHandler
	public String error(MessagingException me, HttpServletResponse response) {

PS:也可以放置@ModelAttribute注解方法用于全局赋值等……

log日志

对异常进行处理,通常就是进行log,可以使用之前学习过的log4j完成:

StackTraceElement[] stackTrace = me.getStackTrace();
for (int i = 0; i < stackTrace.length; i++) {    //可以缩短堆栈深度
	Logger.getLogger(getClass()).info(stackTrace[i]);			
}

注意这个logger是:

import org.jboss.logging.Logger;
这样才有更大的灵活性:当项目不再使用log4j做为日志工具的时候,可以通过jboss无缝切换到其他log工具。

演示:log文件中出现了异常记录……

事务回滚

不要忘了,这是SessionPerRequest复习的一部分。
public void Rollback() {
	// TODO Auto-generated method stub
	try {
		if (transaction.isActive()) {
			Logger.getLogger(ErrorHandle.class).info("事务回滚……");
			transaction.rollback();
		}//else...			
	} catch (Exception e) {
		//log  这时候不能再往外抛异常了
	} finally {
		em.close();    //这是最关键的

HandlerExceptionResolver

可以配置由实现了HandlerExceptionResolver接口的类来处理异常。

F3演示:该接口下已有的实现类,以及抽象类AbstractHandlerExceptionResolver

  • DefaultHandlerExceptionResolver是SpringMVC默认使用的
  • SimpleMappingExceptionResolver需要在springmvc-servlet.xml中配置:
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    	<!-- 默认错误页面 -->
    	<property name="defaultErrorView" value="/exception.html" />
    	<property name="exceptionMappings">
    		<props>
    			<!-- 在异常类型和错位页面之间建立关联 -->
    			<prop key="javax.mail.MessagingException">/email/failed.html</prop>
    		</props>
    	</property>
    	<!-- ex为异常对象,一般供jsp页面使用 -->
    	<property name="exceptionAttribute" value="ex" />
    </bean>
    不同的异常匹配不同的页面
  • ResponseStatusExceptionResolver会解析带有@ResponseStatus注解的异常类
    @ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "用户名或密码错误")
    public class UserNameNotMatchPasswordException extends RuntimeException{
    @ResponseStatus还可以直接用于hanler method,改变其response status:
    @ResponseStatus(code = HttpStatus.BAD_GATEWAY, reason = "网关出错……")
    public ModelAndView on(Model model ) {


作业

  1. 能上传个人资料头像,按用户id重命名,按年月日组织图片目录结构,并能将头像显示在页面
  2. 完成验证码功能(学有余力的同学可以将其封装成宏)
  3. 有序组织错误页面,比如:
    1. (id)不存在的文章单页转到404页面
    2. 不正确的url参数名转到网址错误(WrongUri)页面
    3. ……
文件上传 错误页面 缓存
觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 Java Web开发 中继续学习:

我们的 特色

  • 面向就业
  • 线上线下结合
  • 同学互助
  • 师生共赢

报班 QQ群:273534701

  • 获取视频资料
  • 要求作业点评
  • 参加阶段性测试
  • 模拟面试