您现在的位置是:首页 >技术杂谈 >Spring MVC的高级功能(异常处理器,拦截器,文件操作)网站首页技术杂谈

Spring MVC的高级功能(异常处理器,拦截器,文件操作)

阿瞒有我良计15 2024-07-23 12:01:02
简介Spring MVC的高级功能(异常处理器,拦截器,文件操作)

目录:

  • 异常处理
  • 拦截器
  • 文件上传和下载

1.异常处理(简单异常处理,自定义异常处理,异常处理注解)

简单异常处理器

HandlerExceptionResolver接口 

如果希望对Spring MVC中所有异常进行统一处理,可以使用Spring MVC提供的异常处理器HandlerExceptionResolver接口。Spring MVC内部提供了HandlerExceptionResolver的实现类SimpleMappingExceptionResolver。它实现了简单的异常处理,通过该实现类可以将不同类型的异常映射到不同的页面,当发生异常的时候,实现类根据发生的异常类型跳转到指定的页面处理异常信息。实现类也可以为所有的异常指定一个默认的异常处理页面,当应用程序抛出的异常没有对应的映射页面,则使用默认页面处理异常信息。

代码示例: 

1.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>org.example</groupId>
    <artifactId>_20230602</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
    </dependencies>

</project>

 2.web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

3.defaultExp.jsp

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<html>
<head>
    <title>默认异常处理页面</title>
</head>
<body>
默认异常处理页面--------${exp}
</body>
</html>

4.IOExp.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 10:08
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
IO异常处理页面--------${exp}
</body>
</html>

5.nullPointerExp.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 10:08
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
空指针异常处理页面--------${exp}
</body>
</html>

6.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:context="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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <context:component-scan base-package="cn.hdc.controller"></context:component-scan>

    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>
                <prop key="java.io.IOException">IOExp.jsp</prop>
            </props>
        </property>
        <property name="defaultErrorView" value="defaultExp.jsp"></property>
        <property name="exceptionAttribute" value="exp"></property>
    </bean>

</beans>

7.ExceptionController

package cn.hdc.controller;

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

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

@Controller
public class ExceptionController {
    @RequestMapping("/showNullPointer")
    public void showNullPointer() {
        ArrayList<String> list = null;
        list.add("hello");

    }

    @RequestMapping("showIO")
    public void showIO() throws IOException {
        FileInputStream fileInputStream = new FileInputStream("abc.txt");
    }

    @RequestMapping("showDefault")
    public void showDefault() {
        int i = 1 / 0;
    }
}

8.项目结构:

9.运行结果:

自定义异常处理器 

resolveException()方法

除了使用SimpleMappingExceptionResolver进行异常处理,还可以自定义异常处理器统一处理异常。通过实现HandlerExceptionResolver接口,重写异常处理方法resolveException()来定义自定义异常处理器。当Handler执行并且抛出异常时,自定义异常处理器会拦截异常并执行重写的resolveException()方法,该方法返回值是ModelAndView类型的对象,可以在ModelAndView对象中存储异常信息,并跳转到异常处理页面。

代码示例:

1.首先把spring-mvc.xml中的简单异常处理注解掉:

 2.MyException

package cn.hdc.exception;


public class MyException extends Exception {
    private String message;

    public MyException(String message) {
        super(message);
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

 3.MyExceptionHandler

package cn.hdc.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;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;

@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        String msg = "";
        if (e instanceof MyException) {
            msg = e.getMessage();
        } else {
            Writer out = new StringWriter();
            PrintWriter printWriter = new PrintWriter(out);
            e.printStackTrace(printWriter);
            msg = "网络异常";
        }
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error.jsp");
        modelAndView.addObject("msg", msg);
        return modelAndView;
    }
}

 4.ExceptionController

package cn.hdc.controller;

import cn.hdc.exception.MyException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

@Controller
public class ExceptionController {
    @RequestMapping("/showNullPointer")
    public void showNullPointer() {
        ArrayList<String> list = null;
        list.add("hello");

    }

    @RequestMapping("showIO")
    public void showIO() throws IOException {
        FileInputStream fileInputStream = new FileInputStream("abc.txt");
    }

    @RequestMapping("showDefault")
    public void showDefault() {
        int i = 1 / 0;
    }

    @RequestMapping("/addData")
    public void addData() throws MyException {
        throw new MyException("新增数据异常");
    }
}

5.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:context="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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <context:component-scan base-package="cn.hdc"></context:component-scan>

    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>

    <!--    简单异常处理-->
    <!--    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">-->
    <!--        <property name="exceptionMappings">-->
    <!--            <props>-->
    <!--                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>-->
    <!--                <prop key="java.io.IOException">IOExp.jsp</prop>-->
    <!--            </props>-->
    <!--        </property>-->
    <!--        <property name="defaultErrorView" value="defaultExp.jsp"></property>-->
    <!--        <property name="exceptionAttribute" value="exp"></property>-->
    <!--    </bean>-->

</beans>

6.error.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 10:50
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
自定义异常处理界面----------${msg}
</body>
</html>

7.项目结构

8.运行结果

异常处理注解:

@ControllerAdvice注解的作用

从Spring 3.2开始,Spring提供了一个新注解@ControllerAdvice ,@ControllerAdvice有以下两个作用。

  • 注解作用在类上时可以增强Controller,对Controller中被@RequestMapping注解标注的方法加一些逻辑处理。
  • ControllerAdvice注解结合方法型注解@ExceptionHandler,可以捕获Controller中抛出的指定类型的异常,从而实现不同类型的异常统一处理。

代码示例:

1.ExceptionAdvice

package cn.hdc.controller;

import cn.hdc.exception.MyException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(MyException.class)
    public ModelAndView doMyException(MyException e) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error.jsp");
        modelAndView.addObject("msg", e.getMessage());
        return modelAndView;
    }

    @ExceptionHandler(Exception.class)
    public ModelAndView doOtherException(Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error.jsp");
        modelAndView.addObject("msg", "网络异常");
        return modelAndView;
    }
}

 2.error.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 10:50
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
自定义异常处理界面----------${msg}
</body>
</html>

3.项目结构:

运行结果:

2.拦截器

什么是拦截器?

拦截器(Interceptor )是一种动态拦截Controller方法调用的对象,它可以在指定的方法调用前或者调用后,执行预先设定的代码。拦截器作用类似于Filter(过滤器),但是它们的技术归属和拦截内容不同。Filter采用Servlet技术,拦截器采用Spring MVC技术;Filter会对所有的请求进行拦截,拦截器只针对Spring MVC的请求进行拦截。

拦截器的定义方式 

在Spring MVC中定义一个拦截器非常简单,常用的拦截器定义方式有以下两种。

  • 第一种方式是通过实现HandlerInterceptor接口定义拦截器。
  • 第二种方式是通过继承HandlerInterceptor 接口的实现类HandlerInterceptorAdapter,定义拦截器。

上述两种方式的区别在于,直接实现HandlerInterceptor接口需要重写HandlerInterceptor接口的所有方法;而继承HandlerInterceptorAdapter类的话,允许只重写想要回调的方法。

preHandler()方法

preHandler()方法用于对程序进行安全控制、权限校验等,它会在控制器方法调用前执行。preHandler()方法的参数request是请求对象,response是响应对象,handler是被调用的处理器对象。preHandler()方法的返回值为bool类型,表示是否中断后续操作。当返回值为true时,表示继续向下执行;当返回值为false时,整个请求就结束了,后续的所有操作都会中断(包括调用下一个拦截器和控制器类中的方法执行等)。 

postHandle()方法

postHandle()方法用于对请求域中的模型和视图做出进一步的修改,它会在控制器方法调用之后且视图解析之前执行。postHandle()方法的前2个参数和preHandler()万法的前2个参数一样,分别是请求对象和响应对象。如果处理器执行完成有返回结果,可以通过第3个参数modelAndView读取
和调整返回结果对应的数据与视图信息。

afterCompletion()方法

afterCompletion()方法可以完成一些资源清理、日志信息记录等工作,它会在整个请求完成后执行,即视图渲染结束之后执行。postHandle()方法的前2个参数和preHandler()方法的前2个参数一样,分别是请求对象和响应对象。第3个参数ex是异常对象,如果处理器执行过程中出现异常,会将异常信息封装在该异常对象中,可以在afterCompletion()方法中针对异常情况进行单独处理
需要注意的是,只有在preHandler()方法的返回值为true时,postHandle()方法和afterCompletion()方法才会按上述执行规则执行。 

配置自定义拦截器代码分析

代码示例:

下面通过实现HandlerInterceptor接口自定义拦截器

1.CustomInterceptor

package cn.hdc.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 CustomInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("在handler之前执行preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("在handler之后执行postHandle,但是在视图渲染之前");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("在视图渲染之后执行afterCompletion");
    }
}

 2.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:context="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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <context:component-scan base-package="cn.hdc"></context:component-scan>

    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>

    <!--    简单异常处理-->
    <!--    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">-->
    <!--        <property name="exceptionMappings">-->
    <!--            <props>-->
    <!--                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>-->
    <!--                <prop key="java.io.IOException">IOExp.jsp</prop>-->
    <!--            </props>-->
    <!--        </property>-->
    <!--        <property name="defaultErrorView" value="defaultExp.jsp"></property>-->
    <!--        <property name="exceptionAttribute" value="exp"></property>-->
    <!--    </bean>-->

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/demo1"/>
            <bean class="cn.hdc.interceptor.CustomInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

3.success.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 15:08
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
拦截器跳转页面-----success.jsp
</body>
</html>

运行结果:

----------------------

由结果得知,拦截器成功拦截/demo1,没有拦截/demo2 .

拦截器的执行流程

单个拦截器的执行流程(如果在项目中只定义了一个拦截器,单个拦截器的执行流程如图所示。)

单个拦截器的执行流程分析

从单个拦截器的执行流程图中可以看出,程序收到请求后,首先会执行拦截器中的preHandle()方法,如果preHandle()方法返回的值为false,则将中断后续所有代码的执行。如果preHandle()方法的返回值为true,则程序会继续向下执行Handler的代码。当Handler执行过程中没有出现异常时,接着会执行拦截器中的postHandle()方法。postHandle()方法执行后会通过DispatcherServlet向客户端返回响应,并且在DispatcherServlet处理完请求后,执行拦截器中的afterCompletion()方法;如果Handler执行过程中出现异常,将跳过拦截器中的postHandle()方法,直接由DispatcherServlet渲染异常页面返回响应,最后执行拦截器中的afterCompletion()方法。

下面通过一个案例演示单个拦截器的执行流程 

1.HelloController

package cn.hdc.controller;

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

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String sayHello() {
        System.out.println("handler执行了。。。");
        return "succ.jsp";
    }

    @RequestMapping("/exp")
    public String exp() {
        System.out.println(1 / 0);
        return "succ.jsp";
    }
}

2.spring-mvc.xml

在spring-mvc.xml中使用<mvcinterceptors>元素的子元素<bean>来配置拦截器,配置的拦截器会拦截所有映射到Handler的请求。

<?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:context="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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <context:component-scan base-package="cn.hdc"></context:component-scan>

    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>

    <!--    简单异常处理-->
    <!--    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">-->
    <!--        <property name="exceptionMappings">-->
    <!--            <props>-->
    <!--                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>-->
    <!--                <prop key="java.io.IOException">IOExp.jsp</prop>-->
    <!--            </props>-->
    <!--        </property>-->
    <!--        <property name="defaultErrorView" value="defaultExp.jsp"></property>-->
    <!--        <property name="exceptionAttribute" value="exp"></property>-->
    <!--    </bean>-->

    <!--    <mvc:interceptors>-->
    <!--        <mvc:interceptor>-->
    <!--            <mvc:mapping path="/demo1"/>-->
    <!--            <bean class="cn.hdc.interceptor.CustomInterceptor"></bean>-->
    <!--        </mvc:interceptor>-->
    <!--    </mvc:interceptors>-->

    <mvc:interceptors>
        <!--        全局拦截器-->
        <bean class="cn.hdc.interceptor.MyInterceptor"></bean>
    </mvc:interceptors>

</beans>

3.MyInterceptor

package cn.hdc.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(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle running");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle running");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion running");
    }
}

4.项目结构:

5.succ.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 16:23
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
拦截器执行流程
</body>
</html>

 6.运行结果:

多个拦截器的执行流程

在大型的企业级项目中,可能会定义很多拦截器来实现不同的功能。假设项目中配置了顺序为Interceptor1、Interceptor2的两个拦截器,多个拦截器的执行流程如图所示。

代码示例:

1.MyInterceptor2

package cn.hdc.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 MyInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle2 running");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle2 running");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion2 running");
    }
}

 2.HelloController

package cn.hdc.controller;

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

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String sayHello() {
        System.out.println("handler执行了。。。");
        return "succ.jsp";
    }

    @RequestMapping("/exp")
    public String exp() {
        System.out.println(1 / 0);
        return "succ.jsp";
    }
}

3.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:context="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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <context:component-scan base-package="cn.hdc"></context:component-scan>

    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>

    <!--    简单异常处理-->
    <!--    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">-->
    <!--        <property name="exceptionMappings">-->
    <!--            <props>-->
    <!--                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>-->
    <!--                <prop key="java.io.IOException">IOExp.jsp</prop>-->
    <!--            </props>-->
    <!--        </property>-->
    <!--        <property name="defaultErrorView" value="defaultExp.jsp"></property>-->
    <!--        <property name="exceptionAttribute" value="exp"></property>-->
    <!--    </bean>-->

    <!--    <mvc:interceptors>-->
    <!--        <mvc:interceptor>-->
    <!--            <mvc:mapping path="/demo1"/>-->
    <!--            <bean class="cn.hdc.interceptor.CustomInterceptor"></bean>-->
    <!--        </mvc:interceptor>-->
    <!--    </mvc:interceptors>-->

    <mvc:interceptors>
        <!--        全局拦截器-->
        <bean class="cn.hdc.interceptor.MyInterceptor"></bean>
        <bean class="cn.hdc.interceptor.MyInterceptor2"></bean>
    </mvc:interceptors>

</beans>

4.项目结构

5.MyInterceptor

package cn.hdc.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(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle running");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle running");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion running");
    }
}

6.运行结果:

从多个拦截器的执行流程图中可以看出,当有程序中配置了多个拦截器时,拦截器中的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而拦截器中的postHandle()方法和afterCompletion()方法则会按照拦截器的配置顺序的相反顺序执行。

案例:后台系统登录验证

1.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:context="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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <context:component-scan base-package="cn.hdc"></context:component-scan>

    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>

    <!--    简单异常处理-->
    <!--    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">-->
    <!--        <property name="exceptionMappings">-->
    <!--            <props>-->
    <!--                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>-->
    <!--                <prop key="java.io.IOException">IOExp.jsp</prop>-->
    <!--            </props>-->
    <!--        </property>-->
    <!--        <property name="defaultErrorView" value="defaultExp.jsp"></property>-->
    <!--        <property name="exceptionAttribute" value="exp"></property>-->
    <!--    </bean>-->

    <!--    <mvc:interceptors>-->
    <!--        <mvc:interceptor>-->
    <!--            <mvc:mapping path="/demo1"/>-->
    <!--            <bean class="cn.hdc.interceptor.CustomInterceptor"></bean>-->
    <!--        </mvc:interceptor>-->
    <!--    </mvc:interceptors>-->

    <!--    <mvc:interceptors>-->
    <!--        &lt;!&ndash;        全局拦截器&ndash;&gt;-->
    <!--        <bean class="cn.hdc.interceptor.MyInterceptor"></bean>-->
    <!--        <bean class="cn.hdc.interceptor.MyInterceptor2"></bean>-->
    <!--    </mvc:interceptors>-->

    <!--    拦截所有页面  全局拦截器-->
    <mvc:interceptors>
        <bean class="cn.hdc.interceptor.LoginInterceptor"></bean>
    </mvc:interceptors>

    <!--    视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>

2.UserController

package cn.hdc.controller;

import cn.hdc.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

@Controller
public class UserController {
    //    系统首页
    @RequestMapping("/main")
    public String main() {
        return "main";
    }

    //    登录界面
    @RequestMapping("/toLogin")
    public String toLogin() {
        return "login";
    }

    //    订单详情页面
    @RequestMapping("/orderinfo")
    public String toOrderInfo() {
        return "orderinfo";
    }

    //    登录界面
    @RequestMapping("/login")
    public String login(User user, Model model, HttpSession session) {
        String username = user.getUsername();
        String password = user.getPassword();
        if (username != null && "zs".equals(username) && password != null && "666".equals(password)) {
            session.setAttribute("user", user);
            return "redirect:main";
        } else {
            model.addAttribute("msg", "用户名或密码错误");
            return "login";
        }
    }

    //    退出成功,跳转到登录页面
    @RequestMapping("/logout")
    public String logOut(HttpSession session) {
        session.invalidate();
        return "login";
    }
}

3.LoginInterceptor

package cn.hdc.interceptor;

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

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

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //针对登录和跳转到登录页面的请求进行放行
        String requestURI = request.getRequestURI();
        System.out.println("LoginInterceptor拦截了" + requestURI + "这个界面,接下来进行登录逻辑验证");
        if (requestURI.indexOf("/login") > 0 || requestURI.indexOf("/toLogin") > 0) {
            return true;
        }
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        if (user != null) {
            System.out.println("用户已经登录,可以访问其他页面");
            return true;
        }

        request.setAttribute("msg", "您还没有登录,请先登录");
        System.out.println("用户未登录,将访问login.jsp,重新登录");
        //此处视图解析器不生效。原生javaweb技术
        request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

4.login.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 18:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="post">
    <div>${msg}</div>
    用户名:<input type="text" name="username"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>

5.main.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 17:39
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<li>您好:${user.username}</li>
<li><a href="${pageContext.request.contextPath}/logout">退出</a></li>
<li><a href="${pageContext.request.contextPath}/orderinfo">订单信息</a></li>
</body>
</html>

6.orderinfo.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/6/2
  Time: 18:30
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
您好:${user.username}
<a href="${pageContext.request.contextPath}/logout">退出</a>
<table border="1" width="80%">
    <tr align="center">
        <td colspan="2">订单id:D001</td>
    </tr>
    <tr align="center">
        <td>商品Id</td>
        <td>商品名称</td>
    </tr>
    <tr align="center">
        <td>P001</td>
        <td>三文鱼</td>
    </tr>
    <tr align="center">
        <td>P002</td>
        <td>红牛</td>
    </tr>
</table>
</body>
</html>

7.User

package cn.hdc.pojo;

public class User {
    private String username;
    private String password;

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + ''' +
                ", password='" + password + ''' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

8.项目结构:

9.运行结果:

 

 

点击登录按钮

点击订单信息,跳转到orderinfo.jsp (控制台没有打印信息)

文件上传和下载

代码示例:

1.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>org.example</groupId>
    <artifactId>_20230602</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.9</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>

</project>

2.FileController

package cn.hdc.controller;

import cn.hdc.pojo.Resource;
import cn.hdc.utils.JSONFileUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.FileUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

@Controller
public class FileController {
    @RequestMapping("/fileUpload")
    public String fileUpload(MultipartFile[] files, HttpServletRequest request) throws Exception {
        String path = request.getServletContext().getRealPath("/files/");
        System.out.println(path);
        ObjectMapper objectMapper = new ObjectMapper();
        //判断files为空,如果为空,上传失败,否则才开始进行上传的操作
        if (files != null && files.length > 0) {
            //开始文件上传操作
            // 文件名的集合
            List<Resource> list = new ArrayList<>();
            //遍历每个文件
            for (MultipartFile file : files) {
                //每个文件都需要保存,但是需要判断名字
                //判断文件是否重名,如果重名,做名字的重命名操作
                String filePath = path + "files.json";
                String fileNameJson = JSONFileUtils.readFile(filePath);
                System.out.println(fileNameJson);
                String filename = file.getOriginalFilename();
                System.out.println(filename);
                if (fileNameJson.length() > 0) {
                    //有其他名字,要判断名字
                    //先将json字符串转换成java对象集合
                    list = objectMapper.readValue(fileNameJson, new TypeReference<List<Resource>>() {
                    });
                    for (Resource resource : list) {
                        if (resource.getName().equals(filename)) {
                            String[] split = filename.split("\.");
                            filename = split[0] + "(1)." + split[1];
                        }
                    }
                }

                //保存文件文件的位置应该是跟目录+改后的名字
                file.transferTo(new File(path + filename));
                //文件名信息要保存到files.json中
                list.add(new Resource(filename));
                //转换成json
                fileNameJson = objectMapper.writeValueAsString(list);
                // 写入到文件
                JSONFileUtils.writeFile(fileNameJson, path + "files.json");
            }
            request.setAttribute("msg", "上传成功!");
            return "forward:fileload.jsp";
        }

        request.setAttribute("msg", "上传失败!");
        return "forward:fileload.jsp";
    }

    @RequestMapping(value = "/getFilesName", produces = "text/html;charset=utf-8")
    @ResponseBody
    public String getFilesName(HttpServletRequest request) throws Exception {
        String path = request.getServletContext().getRealPath("/files/files.json");
        String fileNameJson = JSONFileUtils.readFile(path);
        return fileNameJson;
    }

    //解决下载文件时乱码
    public String getFileName(HttpServletRequest request, String filename) throws UnsupportedEncodingException {
        BASE64Encoder base64Encoder = new BASE64Encoder();
        String agent = request.getHeader("User-Agent");
        if (agent.contains("Firefox")) {
            filename = "=?UTF-8" + new String(base64Encoder.encode(filename.getBytes("UTF-8"))) + "?=";
        } else {
            filename = URLEncoder.encode(filename, "UTF-8");
        }
        return filename;
    }

    @RequestMapping("/download")
    public ResponseEntity<byte[]> fileDownload(HttpServletRequest request, String filename) throws IOException {
//        下载文件所在路径
        String path = request.getServletContext().getRealPath("/files/");
//        创建文件对象
        File file = new File(path + File.separator + filename);
//        设置消息头
        HttpHeaders httpHeaders = new HttpHeaders();
//        文件名乱码处理
        filename = getFileName(request, filename);
//        打开文件
        httpHeaders.setContentDispositionFormData("attachment", filename);
//        下载返回的文件数据
        httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//        使用ResponseEntity对象封装返回下载数据
        return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), httpHeaders, HttpStatus.OK);
    }
}

3.Resource实体

package cn.hdc.pojo;

public class Resource {
    private String name;

    public Resource() {
    }

    public Resource(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4.JSONFileUtils工具类

package cn.hdc.utils;

import org.apache.commons.io.IOUtils;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class JSONFileUtils {
    public static String readFile(String filepath) throws Exception {
        FileInputStream fileInputStream = new FileInputStream(filepath);
        return IOUtils.toString(fileInputStream);
    }

    public static void writeFile(String data, String filepath) throws Exception {
        FileOutputStream fileOutputStream = new FileOutputStream(filepath);
        IOUtils.write(data, fileOutputStream);
    }
}

5.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:context="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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

    <mvc:default-servlet-handler/>

    <context:component-scan base-package="cn.hdc"></context:component-scan>

    <!--配置静态资源的访问映射,此配置中的文件,将不被前端控制器拦截 -->
    <mvc:resources mapping="/js/**" location="/js/"/>

    <!--    简单异常处理-->
    <!--    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">-->
    <!--        <property name="exceptionMappings">-->
    <!--            <props>-->
    <!--                <prop key="java.lang.NullPointerException">nullPointerExp.jsp</prop>-->
    <!--                <prop key="java.io.IOException">IOExp.jsp</prop>-->
    <!--            </props>-->
    <!--        </property>-->
    <!--        <property name="defaultErrorView" value="defaultExp.jsp"></property>-->
    <!--        <property name="exceptionAttribute" value="exp"></property>-->
    <!--    </bean>-->

    <!--    <mvc:interceptors>-->
    <!--        <mvc:interceptor>-->
    <!--            <mvc:mapping path="/demo1"/>-->
    <!--            <bean class="cn.hdc.interceptor.CustomInterceptor"></bean>-->
    <!--        </mvc:interceptor>-->
    <!--    </mvc:interceptors>-->

    <!--    <mvc:interceptors>-->
    <!--        &lt;!&ndash;        全局拦截器&ndash;&gt;-->
    <!--        <bean class="cn.hdc.interceptor.MyInterceptor"></bean>-->
    <!--        <bean class="cn.hdc.interceptor.MyInterceptor2"></bean>-->
    <!--    </mvc:interceptors>-->

    <!--    拦截所有页面  全局拦截器-->
    <!--    <mvc:interceptors>-->
    <!--        <bean class="cn.hdc.interceptor.LoginInterceptor"></bean>-->
    <!--    </mvc:interceptors>-->

    <!--    视图解析器-->
    <!--    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
    <!--        <property name="prefix" value="/WEB-INF/"></property>-->
    <!--        <property name="suffix" value=".jsp"></property>-->
    <!--    </bean>-->

    <!--    文件解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="utf-8"></property>
        <property name="maxUploadSize" value="8172000"></property>
    </bean>

</beans>

6.web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

7.fileload.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传和下载</title>
    <script src="${pageContext.request.contextPath}/js/jquery.js" type="text/javascript"></script>
</head>
<body>
<table border="1">
    <tr>
        <td width="200" align="center">文件上传${msg}</td>
        <td width="300" align="center">下载列表</td>
    </tr>
    <tr>
        <td height="100">
            <form action="${pageContext.request.contextPath}/fileUpload" method="post" enctype="multipart/form-data">
                <input type="file" name="files" multiple="multiple"><br/>
                <input type="reset" value="清空"/>
                <input type="submit" value="提交"/>
            </form>
        </td>
        <td id="files"></td>
    </tr>
</table>
</body>
<script>
    $(document).ready(function () {
        var url = "${pageContext.request.contextPath }/getFilesName";
        $.get(url, function (files) {
            var files = eval('(' + files + ')');
            for (var i = 0; i < files.length; i++) {
                $("#files").append("<li>" +
                    "<a href = ${pageContext.request.contextPath }" + "" +
                    "\" + "/download?filename=" + files[i].name + ">" +
                    files[i].name + "</a></li>");
            }
        })
    })
</script>

</html>

8.项目结构:

9.运行结果:

文件上传的知识点:

文件上传表单的满足条件 

大多数文件上传都是通过表单形式提交给后台服务器,因此,要实现文件上传功能,就需要提供一个文件上传的表单,并且该表单必须满足以下3个条件。

  • form表单的method属性设置为post。
  • form表单的enctype属性设置为multipart/form-data。
  • 提供<input type="file" name="filename"/>的文件上传输入框。

文件上传表单的示例代码

上述代码中的文件上传表单除了满足了必须的3个条件之外,还在文件上传输入框中增加了一个HTML5中的新属性multiple。如果文件上传输入框中使用了multiple属性,则在上传文件时,可以同时选择多个文件进行上传,即可实现多文件上传。 

MultipartResolver接口

当客户端提交的form表单中enctype属性为multipart/form-data时,浏览器会采用二进制流的方式来处理表单数据,服务器端会对请求中上传的文件进行解析处理。Spring MVC为文件上传提供了直接的支持,这种支持是通过MultipartResolver (多部件解析器)对象实现的。MultipartResolver是一个接口,可以使用MultipartResolver的实现类CommonsMultipartResolver来完成文件上传工作。

MultipartResolver接口的使用

在Sring MVC中使用MultipartResolver接口非常简单,只需要在配置文件中定义MultipartResolver接口的Bean即可,具体配置方式如下。 

<property>元素

<property>元素可以配置文件解析器类CommonsMultipartResolver的如下属性。

  • maxUploadSize :上传文件最大值(以字节为单位)。
  • maxInMemorySize:缓存中的最大值(以字节为单位)。
  • defaultEncoding :默认编码格式。
  • resolveLazily:推迟文件解析,以便在Controller中捕获文件大小异常。 

配置CommonsMultipartResolver时指定Beanid 

因为初始化MultipartResolver时,程序会在BeanFactory中查找名称为multipartResolver的MultipartResolver实现类,如果没有查找到对应名称的MultipartResolver实现类,将不提供多部件解析处理。所以在配置CommonsMultipartResolver时必须指定该Bean的id为multipartResolver。

 CommonsMultipartResolver如何实现上传功能 

CommonsMultipartResolver并未自主实现文件上传下载对应的功能,而是在内部调用了Apache Commons FileUpload的组件,所以使用Spirng MVC的文件上传功能,需要在项目中导入Apache Commons FileUpload组件的依赖,即commons-fileupload依赖和commons-io依赖。由于commons-fileupload依赖会自动依赖commons-io,所以可以只在项目的pom.xml文件中引入如下依赖。

当完成文件上传表单和文件上传解析器的配置后,就可以在Controller中编写上传文件的方法。在Spring MVC中,上传文件的方法编写十分简单,其代码如下所示。 

MultipartFile接口的常用方法

 什么是文件下载

文件下载就是将文件服务器中的文件传输到到本机上。进行文件下载时,为了不以客户端默认的方式处理返回的文件,可以在服务器端对所下载的文件进行相关的配置。配置的内容包括返回文件的形式、文件的打开方式、文件的下载方式和响应的状态码。其中,文件的打开方式可以通过响应头Content-Disposition的值来设定,文件的下载方式可以通过响应头Content-Type中设置的MIME类型来设定。

使用ResponseEntity对象进行文件下载

上面示例中,设置响应头信息中的MediaType代表的是Interner Media Type(即互联网媒体类型),也叫做MIME类型,MediaType.APPLICATION_OCTET_STREAM的值为application/octet-stream,即表示以二进制流的形式下载数据。HttpStatus类型代表的是Http协议中的状态,示例中的HttpStatus.OK表示200,即服务器已成功处理了请求。 

中文乱码问题

在实现文件下载的功能时,还需要注意文件中文名称的乱码问题。在使用Content-
Disposition设置参数信息时,如果Content-Disposition中设置的文件名称出现中文字符,需要针对不同的浏览器设置不同的编码方式。目前Content-Disposition支持的编码方式有UrlEncode编码、Base64编码、RFC2231编码和ISO编码。本案例不对全部浏览器的编码方式进行设置,只对FireFox浏览器和非FireFox浏览器(如IE)分别进行编码设置.

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。