• 2.0SpringMVC中文件上传、拦截器和异常处理器


    😹 作者: gh-xiaohe
    😻 gh-xiaohe的博客
    😽 觉得博主文章写的不错的话,希望大家三连(✌关注,✌点赞,✌评论),多多支持一下!!!

    🚏 文件上传和下载

    🚀 1、文件下载

        使用ResponseEntity实现下载文件的功能

        @RequestMapping("/testDown")
        public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
            //获取ServletContext对象
            ServletContext servletContext = session.getServletContext();
            //获取服务器中文件的真实路径
            String realPath = servletContext.getRealPath("/static/img/1.jpg");
            System.out.println(realPath);
            //创建输入流
            InputStream is = new FileInputStream(realPath);
            //创建字节数组
            //is.available() 获取输入流所对应文件的所有字节数
            byte[] bytes = new byte[is.available()];
            //将流读到字节数组中
            is.read(bytes);
            //创建HttpHeaders对象设置响应头信息
            MultiValueMap<String, String> headers = new HttpHeaders();
            //设置要下载方式以及下载文件的名字  固定格式
            headers.add("Content-Disposition", "attachment;filename=1.jpg");
            //设置响应状态码
            HttpStatus statusCode = HttpStatus.OK;
            //创建ResponseEntity对象
            /*
            三个参数
    
                bytes:需要下载文件的所有字节(响应体)
                headers:响应头
                statusCode:响应状态码(200,404….)
            */
            ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, headers, statusCode);
            //关闭输入流
            is.close();
            return responseEntity;
        }
    
    • 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

    🚄 2、文件上传

    文件上传要求:不可以用get请求,

    • post请求

    • enctype=“multipart/from-data” ,当前form表单中数据,就不是name=value的方式传输到服务器,而是通过二进制的方式

      SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息

    🚬 上传步骤:

    🚭 添加依赖:

    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🚭 在SpringMVC的配置文件中添加配置:

    <!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
    
    • 1
    • 2

    🚭 控制器方法:

        @RequestMapping("/testUp")
        public String testUp(MultipartFile photo, HttpSession session) throws IOException {
            //获取上传的文件的文件名
            String fileName = photo.getOriginalFilename();
            //获取上传的文件的后缀名   
            // 截取最后一次出现 . 的位置 到最后
            String suffixName = fileName.substring(fileName.lastIndexOf("."));
            //将UUID作为文件名  // replaceAll("-",""); - 替换为 空
            String uuid = UUID.randomUUID().toString().replaceAll("-","");
            //将uuid和后缀名拼接后的结果作为最终的文件名
            fileName = uuid + suffixName;
            //通过ServletContext获取服务器中photo目录的路径
            ServletContext servletContext = session.getServletContext();
            String photoPath = servletContext.getRealPath("photo");
            File file = new File(photoPath);
            //判断photoPath所对应路径是否存在
            if(!file.exists()){
                //若不存在,则创建目录
                file.mkdir();
            }
            // 拼接全路径  File.separator 文件分割符
            //(没有文件服务器,传入到tomcat中)上传 到 部署服务器 的photo目录下(不存在新创建)
            // [E:\tomcat\apache-tomcat-9.0.56\webapps\ROOT\photo\5aa04da74d3d4c5dbe14475bfd5c6d52.png]
            String finalPath = photoPath + File.separator + fileName;
            //上传文件
            photo.transferTo(new File(finalPath));
            return "success";
        }
    
    • 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

    🚏 拦截器

    🚀 1、拦截器的配置

        SpringMVC中的拦截器用于拦截控制器方法的执行

        SpringMVC中的拦截器需要实现HandlerInterceptor

    🚬 SpringMVC.xml 配置 拦截器

    <!--配置方式一: bean-->
    <mvc:interceptors>
    	<!--所写的拦截器 路径-->
    	<!--一个bean就是IOC的组件(对象) 表示某一个类型的对象就是一个拦截器-->
    	<bean class="com.atguigu.interceptor.FirstInterceptor"></bean> 
    </mvc:interceptors>
    
    <!--配置方式二: ref--> 
    <!--ref引用IOC容器中的某个bean的id,引用某一个拦截器的id--> 
    <mvc:interceptors>
        <!--所写的拦截器 类名 需要使用普通组件注解@Component 表示为IOC容器 + 扫描 -->
        <ref bean="firstInterceptor"></ref>
    </mvc:interceptors>
    <!-- 以上两种配置方式都是对DispatcherServlet所处理的所有的请求进行拦截,没有办法设置拦截规则 -->
    
    <!--配置方式三:--> (推荐)
    <mvc:interceptors>
    	<mvc:interceptor>
        	<!--需要拦截的请求--> 
       	 	<mvc:mapping path="/**"/>
        	<!--需要排除的请求,即不需要拦截的请求--> 
        	<mvc:exclude-mapping path="/testRequestEntity"/>
        	<!--和上面的两种写法一样 bean 或者 ref--> 
        	<ref bean="firstInterceptor"></ref>
    	</mvc:interceptor>
    </mvc:interceptors>
    <!--
    	以上配置方式可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求
    -->
    
    • 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

    🚭 / 和 /* 和 /** 的区别

    • / :拦截所有请求 包括静态资源动态请求 但是不拦截jsp
    • /*:拦截所有请求所有的文件夹,但是不包括子文件夹 包括静态资源和动态请求 也拦截jsp
    • /**:的意思是所有文件里面的子文件夹

    🚬 SpringMVC拦截器流程图片:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OJFkBx9b-1656212021942)(文件上传和下载.assets/image-20220625123321996.png)]

    🚬 拦截器和过滤器:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZF41yg3G-1656212021943)(文件上传和下载.assets/image-20220626105110870.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p6pxlv6l-1656212021943)(文件上传和下载.assets/image-20220625123433331.png)]

    🚬 源码

    public class DispatcherServlet extends FrameworkServlet {
    
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
            try {
                try {
                    ModelAndView mv = null;
                    Exception dispatchException = null;
    
                    try {
                        processedRequest = this.checkMultipart(request);
                        multipartRequestParsed = processedRequest != request;
                        mappedHandler = this.getHandler(processedRequest);
                        if (mappedHandler == null || mappedHandler.getHandler() == null) {
                            this.noHandlerFound(processedRequest, response);
                            return;
                        }
    
                        HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                        String method = request.getMethod();
                        boolean isGet = "GET".equals(method);
                        if (isGet || "HEAD".equals(method)) {
                            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                            if (this.logger.isDebugEnabled()) {
                                this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                            }
    
                            if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                                return;
                            }
                        }
    					// 调用当前所有的拦截器的 PreHandle 方法
                        // mappedHandler 执行链 存放的是 执行的控制器方法 + 3个处理控制器方法的拦截器
                        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                            return;
                        }
    					
                        // 控制器方法的执行
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                        if (asyncManager.isConcurrentHandlingStarted()) {
                            return;
                        }
    
                        this.applyDefaultViewName(processedRequest, mv);
                        // 调用当前所有的拦截器的 PostHandle 方法
                        mappedHandler.applyPostHandle(processedRequest, response, mv);
                    } catch (Exception var20) {
                        dispatchException = var20;
                    } catch (Throwable var21) {
                        dispatchException = new NestedServletException("Handler dispatch failed", var21);
                    }
    				//processDispatchResult 里面有个 render方法 来渲染视图
                    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
                } catch (Exception var22) {
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
                } catch (Throwable var23) {
                    this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
                }
    
            } finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    if (mappedHandler != null) {
                        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    }
                } else if (multipartRequestParsed) {
                    this.cleanupMultipart(processedRequest);
                }
    
            }
        }
        
        
            private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
            boolean errorView = false;
            if (exception != null) {
                if (exception instanceof ModelAndViewDefiningException) {
                    this.logger.debug("ModelAndViewDefiningException encountered", exception);
                    mv = ((ModelAndViewDefiningException)exception).getModelAndView();
                } else {
                    Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                    mv = this.processHandlerException(request, response, handler, exception);
                    errorView = mv != null;
                }
            }
    
            if (mv != null && !mv.wasCleared()) {
                this.render(mv, request, response); // 渲染视图来对 ModelAndView进行处理
                if (errorView) {
                    WebUtils.clearErrorRequestAttributes(request);
                }
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
            }
    
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    // triggerAfterCompletion 拦截器的三个方法 在渲染视图完毕之后
                    mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
                }
    
            }
        }
    
    • 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
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    🚄 2、拦截器的三个抽象方法

    🚬 SpringMVC中的拦截器有三个抽象方法:

    • preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法

    • postHandle:控制器方法执行之后执行postHandle()

    • afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

    🚒 3、多个拦截器的执行顺序

    🚬 a>若每个拦截器的preHandle()都返回true

        此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件配置顺序有关:

        preHandle()会按照配置的顺序执行,而postHandle()afterComplation()会按照配置的反序执行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3GWwVfCK-1656212021944)(文件上传和下载.assets/image-20220625123517430.png)]

    🚬 b>若某个拦截器的preHandle()返回了false

        preHandle()返回false和它之前的拦截器的preHandle()都会执行postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行

    • 举例:拦截器 1 2 3 4 5 3 未放行
      • preHandle 123
      • postHandle 一个都不会执行
      • afterCompletion 1 2 执行

    🚏 异常处理器

    🚀 1、基于配置的异常处理

        SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver

        HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolverSimpleMappingExceptionResolver

        作用:在控制器方法执行时,出现异常,返回新的ModelAndView,跳转到制定页面

    SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
            	<!--
            		properties的键表示处理器方法执行过程中出现的异常
            		properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
            	-->
                <prop key="java.lang.ArithmeticException">error</prop>
            </props>
        </property>
        <!--
        	exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享
        -->
        <property name="exceptionAttribute" value="ex"></property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    error 页面
    
    <body>
    出现错误
    <p th:text="${ex}"></p>
    </body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🚄 2、基于注解的异常处理

    //@ControllerAdvice将当前类标识为异常处理的组件
    @ControllerAdvice
    public class ExceptionController {
    
        //@ExceptionHandler用于设置所标识方法处理的异常
        @ExceptionHandler(ArithmeticException.class)
        //ex表示当前请求处理中出现的异常对象
        public String handleArithmeticException(Exception ex, Model model){
            model.addAttribute("ex", ex);
            return "error";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    springboot- redis常见数据存取
    Apache网页优化
    Taurus.MVC WebMVC 入门开发教程2:一个简单的页面呈现
    Java-多线程-CompletionService(优先处理)
    基于微信小程序的在线测试系统
    haproxy工具,负载均衡配置,反向代理配置,动静分离,高可用等等
    Dubbo基本原理机制
    JVM调优常用工具及命令
    最长回文子串
    go build解决missing go.sum.entry
  • 原文地址:https://blog.csdn.net/gh_xiaohe/article/details/125467842