SpringMVC-文件上传-异常处理-拦截器

文件上传

1.1 浏览器端要求(通用浏览器的要求)
  • 表单提交方式 post
  • 提供文件上传框(组件) input type=“file”
  • 表单的entype属性必须为 multipart/form-data(没有这个属性值的话, 文件的内容是提交不过去的)
1.2 服务器端要求

​ 要使用request.getInputStream()来获取数据.
注意:

  • 若表单使用了 multipart/form-data ,使用原生request.getParameter()去获取参数的时候都为null

我们做文件上传一般会借助第三方组件(jar, 框架 SpringMVC)实现文件上传.

1.3 常见的文件上传jar包和框架

​ serlvet3.0
​ commons-fileupload : apache出品的一款专门处理文件上传的工具包
​ struts2(底层封装了:commons-fileupload)
​ SpringMVC(底层封装了:commons-fileupload)

小结

  1. 上传商品图片、excel(导入数据)。。。
  2. 前端三要素 form post, encpt=multipart/form-data, input type=file
  3. 使用commons-fileupload, 原生的api处理太复杂了,它帮我们简化了开发

传统方式文件上传

步骤:

  1. 创建maven web工程, 添加springmvc的依赖
  2. 把commons-fileupload坐标导入进来
  3. 在控制器的方法的形参里面定义和文件相关的变量 MultipartFile
  4. 把文件存到服务器
  5. 配置文件解析器 (commons-file)

实现:
创建Maven工程,添加依赖


    commons-fileupload
    commons-fileupload
    1.3.1

创建前端index.jsp页面

传统文件上传


创建FileUploadController控制器

@Controller
public class FileUploadController {

    @RequestMapping("/upload")
    public String upload(HttpServletRequest req, MultipartFile uploadFile, String desc) throws IOException {
        System.out.println("desc=" + desc);
        // 文件名
        String filename = uploadFile.getOriginalFilename();
        // 生成唯一文件名
        String uniqueName = UploadUtils.getUUIDName(filename);
        // 获取保存文件的路径
        String baseDir = req.getSession().getServletContext().getRealPath("/upload");
        // /upload/2/A
        // 产生2层目录结构, UploadUtils这个类在资料中有
        String dir = UploadUtils.getDir();
        // 创建保存的目录
        File saveDir = new File(baseDir, dir);
        if(!saveDir.exists()){
            saveDir.mkdirs();// 创建多层结构
        }
        // 保存文件
        uploadFile.transferTo(new File(saveDir, uniqueName));
        return "success";
    }
}

在springmvc.xml配置文件解析器
注意:文件上传的解析器 id 是固定的,不能起别的名称,否则无法实现请求参数的绑定。(不光是文件,其他
字段也将无法绑定)

    
    
         
        
    

小结

  1. 前端三要素 form post, encpt=multipart/form-data, input type=file
  2. Controller使用MultipartFile接收文件,形参名称必须与 前端中的name 一样,严格大小写
  3. 配置使用commons-fileupload
  4. 分目录存储

跨服务器方式的文件上传

分服务器的目的

在实际开发中,我们会有很多处理不同功能的服务器(注意:此处说的不是服务器集群)。 例如:
​ 应用服务器:负责部署我们的应用
​ 数据库服务器:运行我们的数据库
​ 缓存和消息服务器:负责处理大并发访问的缓存和消息
​ 文件服务器:负责存储用户上传文件的服务器。
分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。

跨服务器方式的文件上传图解

SpringMVC-文件上传-异常处理-拦截器_第1张图片
准备两个服务器, 文件服务的要修改tomcat的的conf目录下的web.xml, 添加readonly参数为false
SpringMVC-文件上传-异常处理-拦截器_第2张图片

文件服务器步骤:

  1. 准备文件服务器, 复制一份tomcat, 修改conf/web.xml 添加 readonly false
  2. 创建web工程文件服务器的应用,布署到这个tomcat上来, 创建 upload目录
    SpringMVC-文件上传-异常处理-拦截器_第3张图片

实现

在springmvc fileupload工程

  • 添jersey依赖
  		<dependency>
  			<groupId>com.sun.jerseygroupId>
  			<artifactId>jersey-coreartifactId>
  			<version>1.18.1version>
  		dependency>
  		<dependency>
  			<groupId>com.sun.jerseygroupId>
  			<artifactId>jersey-clientartifactId>
  			<version>1.18.1version>
  		dependency>
  • 前端页面
上传到文件服务器
<form action="/upload2" enctype="multipart/form-data" method="post">
    <input type="file" name="uploadFile"><br/>
    <input name="desc"><br/>
    <input type="submit" value="提交">
form>
  • 控制器
    /**
     * 把上传的文件上传到文件服务器
     * @param req
     * @param uploadFile
     * @param desc
     * @return
     * @throws IOException
     */
    @RequestMapping("/upload2")
    public String upload2(HttpServletRequest req, MultipartFile uploadFile, String desc) throws IOException {
        System.out.println("desc=" + desc);
        // 文件名
        String filename = uploadFile.getOriginalFilename();
        // 生成唯一文件名
        String uniqueName = UploadUtils.getUUIDName(filename);
        // 保存到文件服务器
        // 文件服务器的连接
        String url = "http://localhost:9090/upload/";
        // jersey客户諯
        Client client = Client.create();
        // 定位存储的目录, 服务器的地址
        // http://localhost:9090/upload/asadfasdfasf.jpg
        WebResource webResource = client.resource(url + uniqueName);
        // 上传
        webResource.put(uploadFile.getBytes());
        return "success";
    }

小结

为什么要分服务器存储?

  • 安全
  • 维护方便
  • 服务器性能充分的利用
  1. 准备文件服务器
    • 准备文件服务器, 复制一份tomcat, 修改conf/web.xml 添加 readonly false, 记得要保存
    • 创建web工程文件服务器的应用,布署到这个tomcat上来, 创建 upload目录
  2. 修改文件上传代码
    使用jersey客户,上传文件

异常处理

分析

​ 系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
SpringMVC-文件上传-异常处理-拦截器_第4张图片

springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
步骤:

  1. 自定义异常类 已知错误。作用:终止已知不符合业务逻辑的继续执行,区分系统异常还是业务异常
  2. 创建异常处理类实现HandlerExceptionResolver接口
  3. 配置让springMVC使用这个接口
  4. 测试

代码实现

2.3.1 自定义异常类

目的: 统一的管理异常, 方便统一管理错误提示语

/**
 * 自定义异常类
 */
public class SysException extends RuntimeException {
    public SysException(String msg){
        super(msg);
    }
}

2.3.2 自定义异常处理器

/**
 * Description: 全局异常处理
 */
public class SysExceptionResolver implements HandlerExceptionResolver {

    /**
     * 处理异常
     * @param request
     * @param response
     * @param handler
     * @param ex  controller往上抛的异常
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 返回值有哪些?
        // jsp, request转发, response 重定向, reponse.write 返回json数据
        ModelAndView mv = new ModelAndView();
        // 对已知道的异常,只要提示信息
        String message = null;
        if (ex instanceof SysException){
            SysException sysException = (SysException) ex;
            // 自己报错的信息
            message = ex.getMessage();
        }else{
            // 对未知的异常,包装下再返回给页面
            message = "发生未知异常,请联系管理员";
        }
        // 返回给前端的提示信息
        mv.addObject("message",message);
        mv.setViewName("error");
        ex.printStackTrace();// 工作中,不能直接调用这句话.
        return mv;
    }
}

2.3.3 配置异常处理器

  • 在springmvc.xml配置

    <bean id="sysExceptionResolver" class="com.itheima.exception.SysExceptionResolver"/>

小结

  1. 自定义异常:终于已经不符合业务逻辑的继续执行,友好的提示页面
  2. 自定义异常继承RuntimeException
  3. 全局异常处理器 实现HandlerExceptionResolver接口,对异常封装处理

拦截器

拦截器概述

​ Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器(自己编写的Controller)进行预处理和后处理。用户可以自己定义一些拦截器来实现特定的功能。谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的它和过滤器是有几分相似,但
是也有区别,接下来我们就来说说他们的区别:
SpringMVC-文件上传-异常处理-拦截器_第5张图片

自定义拦截器入门

  • 编写个一测试用的ControllerDemo

    @Controller
    public class ControllerDemo {
    
        @RequestMapping("/demo01")
        public String demo01(){
            System.out.println("ControllerDemo.demo01执行了......");
            return "success";
        }
    }
    
  • 编写一个普通类HandlerInterceptorDemo1实现 HandlerInterceptor 接口
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
    /**
     * 前置拦截, 在调用controller的方法前执行
     * @param request
     * @param response
     * @param handler
     * @return true: 放行, false:阻止
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // response 可以重定向, 用户登陆认证(session中不存存)
        System.out.println("HandlerInterceptorDemo1.preHandle 执行了");
        return true;
    }
}

  • 在springmvc.xml配置拦截器
    
    <mvc:interceptors>

        <mvc:interceptor>
            
            <mvc:mapping path="/**"/>
            <bean id="intercepter01" class="com.itheima.interceptor.HandlerInterceptorDemo1">bean>
        mvc:interceptor>
    mvc:interceptors>

小结

  1. 拦截器,在进入 controller之前拦截。
  2. 实现HandlerInterceptor接口, preHandler方法, 返回true则可以调用controller方法,false则不会调用controller
  3. springmvc.xml配置它
  4. 作用:前置处理:权限控制(认证,有没有登陆),微服的链路跟踪, 记录用户的访问日志

自定义拦截器进阶

路径

  1. 拦截器的放行
  2. 拦截后跳转
  3. 拦截器的路径
  4. 拦截器的其它方法
  5. 多个拦截器执行顺序

拦截器的放行
SpringMVC-文件上传-异常处理-拦截器_第6张图片

拦截后跳转

​ 拦截器的处理结果,莫过于两种:

​ 放行: 如果后面还有拦截器就执行下一个拦截器,如果后面没有了拦截器,就执行Controller方法

​ 拦截: 但是注意,拦截后也需要返回到一个具体的结果(页面,Controller)。

  • 在preHandle方法返回false,通过request进行转发,或者通过response对象进行重定向,输出
public class Intercepter01 implements HandlerInterceptor {
    @Override
    //在达到目标方法之前执行(拦截的方法)
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle 执行了...");
        //转发到拦截后的页面
        //request.getRequestDispatcher("/intercepter01.jsp").forward(request,response);
        //转发到controller
        request.getRequestDispatcher("/demo01/fun02").forward(request,response);
        return false;//返回true放行, 返回false拦截
    }

拦截器的路径

SpringMVC-文件上传-异常处理-拦截器_第7张图片

拦截器的其它方法

  • afterCompletion 在目标方法完成视图层渲染后执行。
  • postHandle 在被拦截的目标方法执行完毕获得了返回值后执行。
  • preHandle 被拦截的目标方法执行之前执行。
public class Intercepter01 implements HandlerInterceptor {
    @Override
    //在达到目标方法之前执行(拦截的方法)
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle 执行了...");
        return true;//返回true放行, 返回false拦截
    }
    @Override
    //在目标方法执行完成之后,完成页面渲染之前执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle 执行了...");
    }

    @Override
    //完成页面渲染之后执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("afterCompletion 执行了...");
    }
}

执行结果
在这里插入图片描述

多个拦截器执行顺序

我们可以配置多个拦截器, 所以就存在一个优先级问题了.多个拦截器的优先级是按照配置的顺序决定的。

SpringMVC-文件上传-异常处理-拦截器_第8张图片

  • 配置顺序
    
    <mvc:interceptors>
        <mvc:interceptor>
            
            <mvc:mapping path="/**"/>
            
            <mvc:exclude-mapping path="/demo01/fun02"/>
            <bean id="intercepter01" class="com.itheima.web.interceptor.Intercepter01">bean>
        mvc:interceptor>
        <mvc:interceptor>
            
            <mvc:mapping path="/**"/>
            
            <mvc:exclude-mapping path="/demo01/fun02"/>
            <bean id="intercepter02" class="com.itheima.web.interceptor.Intercepter02">bean>
        mvc:interceptor>
    mvc:interceptors>
  • 结果
    SpringMVC-文件上传-异常处理-拦截器_第9张图片

小结

拦截器是一个链式模式
只有preHandler方法才有返回true 放行,false拦截
拦截器的跳转 return false, request转发,response重定向
preHandle->postHandle->afterCompletion
多个执行的顺序按配置文件配置的顺序,从上而下执行。多个时,先进(preHandle)后出(postHandle,afterCompletion)

你可能感兴趣的:(mvc)