Spring-MVC框架之文件上传&拦截器&异常处理

Spring-MVC框架之文件上传&拦截器&异常处理

文件上传

springMVC框架也实现了文件上传功能,底层是融合了commons-fileupload这个功能,实现起来非常简单易用。

前端页面将form表单的method设置为post,enctype属性设置为multipart/form-data,并且在jsp中的input标签中type属性设置为file,

平时我们使用的form表单是纯文本,也就是enctype=application/x-www-form-urlencoded这个类型,这种类型,在内容格式是以键值对形式进行传递,当enctype变为multipart/form-data的时候正文内容就被分为多个部分,同时request.getParameter将失效。
此时我们用到了spring整合后的fileupload功能,首先导入依赖jar包
pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>javaee</groupId>
    <artifactId>day07_springMVC_fileUpload</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.3</version>
        </dependency>
    </dependencies>
</project>

然后开始配置spring-mvc文件,特别注意文件上传的bean配置id必须为multipartResolver,否则会报空指针异常

spring-mvc.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:contxt="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--开启注解扫描-->
    <contxt:component-scan base-package="com.dyh"/>

    <!--开启json格式-->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <!--json数据乱码-->
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!--文件上传配置-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置默认编码格式-->
        <property name="defaultEncoding" value="UTF-8"/>
        <!--设置单个文件最大上传大小,以字节位单位-->
        <property name="maxUploadSizePerFile" value="5242880"/>
        <!--设置所有文件最大上传大小,以字节位单位-->
        <property name="maxUploadSize" value="5242880"/>
    </bean>

    
</beans>

写一个controller去进行上传文件
controller

package com.dyh.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.UUID;

@Controller
@RequestMapping("/upload")
public class FileUpload {


    @RequestMapping("/upload01")
    @ResponseBody                   //返回的类型是json字符串
    public String uploadFile01(String name, MultipartFile uploadFile) throws Exception {
//        输出上传的用户名和文件对象
        System.out.println(name);
//        如果文件的结尾不是.txt结尾则抛出异常,返回到异常页面
        if (!uploadFile.getOriginalFilename().endsWith(".txt")) throw new Exception();
//        生成随机文件名
        String newName = UUID.randomUUID() + uploadFile.getOriginalFilename();
//        上传文件到指定路径
        uploadFile.transferTo(new File("D:/test/" + newName));
        return "上传成功!";
    }

}

在这里我们有使用jsp页面进行测试,我是通过postman进行测试
运行结果
在这里插入图片描述
上传的是图片的时候就无法上传。
下面在附上一张上传成功的截图
在这里插入图片描述
如果是多文件上传,post表单name相同是,controller使用数组接收即可

package com.dyh.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.UUID;

@Controller
@RequestMapping("/upload")
public class FileUpload {


    @RequestMapping("/upload01")
    @ResponseBody                   //返回的类型是json字符串
    public String uploadFile01(String name, MultipartFile[] uploadFile) throws Exception {
	    for(MultipartFile file : uploadFile){
	//        如果文件的结尾不是.txt结尾则抛出异常,返回到异常页面
	        if (!file.getOriginalFilename().endsWith(".txt")) throw new Exception();
	//        生成随机文件名
	        String newName = UUID.randomUUID() + file.getOriginalFilename();
	//        上传文件到指定路径
	        file.transferTo(new File("D:/test/" + newName));
	    }

        return "上传成功!";
    }

}

拦截器

关于拦截器,从功能上来讲,它和过滤器是是一样的,本质上都用于队处理器进行预处理和后处理。但是从底层来说,他们的实现原理还是有区别的,也使面试时经常会问到的问题。

名称说明
过滤器是servlet规范中的一部分,在任何javaweb工程中都可以使用,在url-pattern配置了/*后,可以对所有要访问的资源进行拦截
拦截器是springMVC框架提供的,只有在springMVC中才可以使用,一般只对控制器中的方法进行拦截,但是通过一定手段也可以拦截所有资源

我们只需要实现HandlerInterceptor接口, 里面的方法都被default修饰过了,所以可以选择性实现里面的preHandle()(前置拦截),postHandle()(返回前拦截),afterCompletion()(后置拦截)方法,用法和AOP有些相似,如果在执行中出现异常则postHandle()不执行。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

手动写一个拦截器

package com.dyh.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

//    前置拦截
    @Override
    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) {
        System.out.println("模拟前置拦截");
        return true;
    }

//    返回拦截
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        System.out.println("模拟返回前拦截");
    }

//    后置拦截
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("模拟后置拦截");
    }
}

随后在配置文件中加上这么一段配置

<!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--对所有方法进行拦截-->
            <mvc:mapping path="/**"/>
            <!--配置自己写的拦截器-->
            <bean class="com.dyh.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

测试的controller

package com.dyh.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {

    /**
     * 监听器快速入门
     */
    @ResponseBody
    @RequestMapping("/test")
    public void test() {
        System.out.println("模拟执行某方法。。。");
    }


}

访问后执行结果
在这里插入图片描述
可以看到我们对这个方法进行了拦截处理,我们再执行过程中手写一个异常,测试下返回前拦截在遇到异常后是否会执行。

/**
     * 监听器快速入门
     */
    @ResponseBody
    @RequestMapping("/test")
    public void test() throws Exception {
        System.out.println("模拟执行某方法。。。");
        //手动抛出异常
        throw new Exception();
    }

运行结果
在这里插入图片描述
这里和AOP一样,返回前的增强和返回前的拦截在遇到异常后都不执行。

异常处理

我们在遇到异常时,不能总是给用户返回404,405,500等错误页面,这样用户体验是非常差劲的。为了避免这种情况,spring也拥有自己的异常处理机制。

spring总共有三种异常处理机制

1:使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver
2:实现Spring的异常处理接口HandlerExceptionResolver 自定义异常处理器
3:使用@ExceptionHandler注解实现异常处理

简单异常处理器

第一种我们可以在配置文件中写入这么一段配置,用来配置异常情况下的处理方式

<!--配置简单映射异常处理器-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!--默认错误的跳转页面-->
        <property name="defaultErrorView" value="error"/>
        <!--多条错误页面设置-->
        <property name="exceptionMappings">
            <map>
                <!--配置异常的全限定类名和返回的错误页面-->
                <entry key="java.lang.RuntimeException" value="error2"/>
                <entry key="java.lang.ClassCastException" value="error3"/>
            </map>
        </property>
    </bean>

这样在出现指定错误时,将会跳转到相应的错误页面。但是缺点就是不灵活,不能给页面传递一些错误信息。

自定义异常处理器

第二种方式相比第一种要自由的多,我们可以实现spring提供的HandlerExceptionResolver接口,实现resolveException()方法,返回值是ModelAndView,参数有HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex,可以自由的在里面进行异常类型判断,进行页面的跳转和错误信息的传递。

自定义异常类

package com.dyh.exception;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义异常类
 */
 //通过注解将自定义异常类交给spring管理,如果发现异常将使用这种方式进行返回。
@Component("myException")	
public class MyException implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        if (e instanceof ClassCastException){
            modelAndView.setViewName("error");
            modelAndView.addObject("errorInfo","类型转换异常");
            return modelAndView;
        }else if (e instanceof RuntimeException){
            modelAndView.setViewName("error2");
            modelAndView.addObject("errorInfo","运行时异常");
            return modelAndView;
        }else {
            modelAndView.setViewName("error3");
            modelAndView.addObject("errorInfo","其他异常");
            return modelAndView;
        }
    }
}

通过这种方式我们可以通过各类异常返回异常信息,和异常跳转的错误页面。

@ExceptionHandler注解

第三种使用@ExceptionHandler注解实现异常处理,自己写一个自定义异常类,随后加上@ExceptionHandler注解声明这是一个异常类,随后如果哪个controller需要处理异常,就继承这个自定义异常。

package com.dyh.exception;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义异常类
 */
@ExceptionHandler
public class MyException{
    public String ex(HttpServletRequest request, Exception ex) {
        request.setAttribute("ex", ex);
        // 根据不同错误转向不同页面
        if(ex instanceof RuntimeException) {
            return "error";
        }else if(ex instanceof ClassCastException) {
            return "error1";
        } else {
            return "error2";
        }
    }
}

使用这种方法简单,扩展性好,但是缺点也同样明显,在异常处理时,不能捕获除这些异常以外的数据。所以不建议使用。

总结

  1. 文件上传:使用form表单的post请求进行提交,form表单格式调整为多部分form表达。在配置文件上传的bean配置时,id必须写为multipartResolver,否则回报空指针异常。
    多文件上传时将前端文件类型的name设置为相同的名字,后端使用数组接收,循环遍历上传即可。
  2. 拦截器:自定义拦截器,需要继承HandlerInterceptor 接口,在xml进行配置拦截器,可以配置多个映射路径和排除路径,也可以配置多个拦截器,形成拦截链。
  3. 异常处理:三种异常处理方式,简单异常处理器,自定义异常处理器,注解配置异常。
    简单异常处理器:指定各类异常跳转的错误页面,不灵活,不能自定义异常信息。
    自定义异常处理器:同样可以根据异常类型返回不同错误界面,并且可以设置自定义异常提示信息
    @ExceptionHandler注解:自己写异常类,并且加上ExceptionHandler注解,当controller需要使用异常处理机制时继承该类。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值