Spring11

SpringMVC文件上传和下载

当SpringMVC的核心控制器DispatcherServlet对象的设置为/的时候,需要设置对静态资源的访问,比如图片资源, js资源,html资源等

静态资源的处理方案:
方法1:
在SpringMVC的配置文件中静态资源处理方案:<mvc:default-servlet-handler/>,将静态资源的处理委托给服务器处理

tomcat本身是能处理静态资源的访问比如html,图片,js文件等,tomcat的web.xml文件中有一个servlet,名称是 default作用是能够处理静态资源,以及处理那些未被映射到其他servlet的请求,default的< url-pattern >就是 /,即所有请求。

方法2:
在Spring3.0后,Spring中有专门处理静态资源的处理方式,同样在SpringMVC配置文件添加配置:

<mvc:resources mapping=“/static/**” location=“/statics/” />

location就是资源所在的文件夹,mapping是映射的访问路径,也就是说允许具体的存放位置和访问路径不一致。另外属性cache-period用于设置缓存时间,单位为秒

<mvc:resources mapping="/images/*" location="/images/" cache-period="#{7*24*60*60}"/>

下载:
业务需求:首先显示upload目录下的所有文件,然后点击对应的图片执行下载

@Controller
public class UploadController {
    @Value("#{contants.contentType}")
    private String allowedFile="";
    @GetMapping("/upload")
    public void upload(){
    }
    @PostMapping("/upload")
    public String upload(MultipartFile pic,HttpServletRequest request)throws Exception{
        /*
        getOriginalFilename():String 获得原始的文件名,不包含文件夹
        isEmpty():boolean判断是否上传了文件,如果没有选择文件上传或者文件长度为0的话,此时结果为true
        getContentType():String 获得上传文件的MIME文件类型,注意不是后缀。因为文件后缀可以任意修改,
        所以通过文件后缀判断上传文件内容类型不安全
        transferTo(File file) 将文件更名上传至指定目录中
        getName():String 获取表单中input的name值
        getBytes():byte[] 获取上传文件内容的byte数组
        getInputStream():InputStream 获取上传文件的InputStream对象

        具体上传处理由多部份上传处理组件负责:
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver具体的上传组件
    名称,需要注意SpringMVC不是仅仅使用commons-fileload执行上传" p:maxUploadSize="#{2*1024*1024}允许上传文件的最大大小,单位字节,
    如果超出指定大小则会出现MaxUploadSizeExceededException" p:resolveLazily="true是否支持延迟处理" p:defaultEncoding="UTF-8
    所使用的默认编码字符集" p:maxInMemorySize="#{10*1204*1024}允许使用的内存大小,如果上传文件超过这个值,则会使用硬盘缓存临时文件"/>
         */

        System.out.println(pic.getOriginalFilename());
        System.out.println("是否为空"+pic.isEmpty());
        System.out.println(pic.getContentType());
        ServletContext application= request.getServletContext();
        String realPath=application.getRealPath("upload/");
        System.out.println(realPath);

        //针对文件内容类型进行合法性验证
        String content=pic.getContentType();//获取文件的内容MIME类型
        if(allowedFile.indexOf(content)<0){
//            errors.reject(null,"不允许上传的文件内容");
            request.setAttribute("msg","不允许上传的文件内容");
            return "upload";
        }

        //具体的上传处理
        File file=new File(realPath);
        if(!file.exists())
            file.mkdirs();
        String fileExt=pic.getOriginalFilename();
        fileExt=fileExt.substring(fileExt.lastIndexOf("."));
        String ss=UUID.randomUUID().toString();
        ss=ss.replaceAll("-","");
        file=new File(realPath,ss+fileExt);
        pic.transferTo(file);
        return "success";


        /*
        后续问题:当上传文件超出指定大小时,会抛出MaxUploadSizeExceededException异常。

SpringMVC框架处理异常有多种方式,最常用方式是使用@ExceptionHandler注解处理异常。
    使用注解@ExceptionHandler可以将一个方法指定为异常处理方法。该注解只有一个可选属性value,为一个Class<?>数组,用于指定该注解的方法所
    要处理的异常类,即所要匹配的异常。
    而被注解的方法,其返回值可以是ModelAndView、String或 void,方法名随意,方法参数可以是Exception及其子类对象、HttpServletRequest、
    HttpServletResponse等。系统会自动为这些方法参数赋值。
    对于异常处理注解的用法,也可以直接将异常处理方法注解于Controller之中。就近原则

    SpringMVC允许定义@ControllerAdvice类型的处理器针对应用中的异常进行统一处理

@ControllerAdvice  用于声明当前类是一个统一处理类
public class ExceptionHander {
    定义处理异常的方法,当异常发生后,执行这个方法,方法的定义和Controller类中方法的定义。在注解ExceptionHandler中声明某个方法处理哪种
    类型的异常,例如处理NameException类型的异常。方法参数Exception表示接收Controller抛出的异常对象,
        @ExceptionHandler(value = NameException.class) 表示当NameException的异常被抛出时,执行此方法
        public ModelAndView doNameException(Exception e){
            System.out.println("我被抓写了"+e);
            ModelAndView mv=new ModelAndView();
            mv.addObject("tips","这个姓名只能是蓝鸥");
            mv.setViewName("nameError");
            return mv;
      }

        多文件上传使用数组或者集合
    public String upload(MultipartFile[] pic,HttpServletRequest request)throws Exception{

        总结上传处理:
        1、form表单要求使用method=post enctype=multipart/form-data
        2、需要添加上传处理组件multipartResolver
        3、针对上传文件大小的限制可以配置在上传组件中,上传组件以异常的方式处理不合法的文件大小。
        4、在Controller中添加@ExceptionHandler处理不合法的文件大小异常
        5、自行编程实现文件内容类型的合法性判断
         */
    }
    @ExceptionHandler({MaxUploadSizeExceededException.class, FileUploadException.class})
    public String handleExceededException(Exception exception, Model model){
        //返回值String是逻辑地址名,参数exception用于接收Controller方法抛出的异常对象,参数model用于向页面传递数据
        model.addAttribute("msg", exception.getMessage());

        StringBuilderWriter writer=new StringBuilderWriter();
        exception.printStackTrace(new PrintWriter(writer));
        System.out.println(writer.toString());
        model.addAttribute("errorMsg",writer.toString());

        return "error";
    }

    public String getAllowedFile() {
        return allowedFile;
    }

    public void setAllowedFile(String allowedFile) {
        this.allowedFile = allowedFile;
    }
}

SpringMVC的拦截机制

Servlet编程中Filter可以实现权限检查之类的拦截机制 AOP

SpringMVC中的interceptor拦截器,它主要的作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在:
处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前。当然处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

拦截器可以看做是多个Controller中公用的功能,集中到拦截器统一处理。使用的是aop的思想
1)拦截器是springmvc中的一种,需要实现HandlerInterceptor接口。
2)拦截器和过滤器类似,功能方向侧重点不同。 过滤器是用来过滤器请求参数,设置编码字符集等工作。拦截器是拦截用户的请求,做请求做判断处理的。
3)拦截器是全局的,可以对多个Controller做拦截。
一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
拦截器常用在:用户登录处理,权限检查, 记录日志。

开发步骤:
1、定义一个类实现HandlerInterceptor接口

public class LogInterceptor implements HandlerInterceptor {
    //在目标Controller之前之前执行的方法
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("==============控制器执行之前执行的方法");
        System.out.println("请求对象:"+request+",注意:虽然不能修改parameter参数,但是如果需要可以依靠request.setAttribute传递数据");
        request.setAttribute("msg1","log01");
        System.out.println("响应对象:"+response+",允许由拦截器生成对应的响应,而不执行具体的目标Controller");
        System.out.println("调用的目标对象:"+handler);
        // 要求返回boolean,如果返回true则表示继续向下执行;如果返回false则表示立即返回
        return true;
    }
    //这个方法在Controller执行结束,跳转到目标页面执行之前执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("==============控制器执行之后,jsp页面执行之前  执行的方法");
        System.out.println("请求对象:"+request);
        request.setAttribute("msg2","post....");
        System.out.println("响应对象:"+response);
        System.out.println("调用的控制器对象:"+handler);
        System.out.println("控制器返回的ModelAndView对象:"+modelAndView);
    }
    //这个方法在jsp执行之后执行,主要用于资源的回收或者异常信息的拦截记录操作。注意仅仅只是拦截异常,并不会将异常消费掉
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("==============jsp页面执行之后执行的方法");
        System.out.println("请求对象:"+request);
        request.setAttribute("msg3","after....");
        System.out.println("响应对象:"+response);
        System.out.println("调用的控制器对象:"+handler);
        System.out.println("执行过程中出现的异常:"+ex);
    }
}

2、针对拦截器进行注册,将对应的请求路径和拦截器建立对应关系

mvc:interceptors
mvc:interceptor
<mvc:mapping path=“/test”/>

</mvc:interceptor>
</mvc:interceptors>

多个拦截器的执行顺序: 【重点】
按照的配置文件中的配置顺序执行拦截器
prehandle1–>prehandle2 拦截器链概念
controller
postHandle2–>postHandle1
jsp
afterCompletion2–>afterCompletion1

如果需要阻止拦截器链的执行可以使用前一个拦截器prehandle返回false执行,但是需要前一个拦截器生成响应
如果在拦截器2的postHandle中产生异常,则前面的拦截器的postHandle方法不再执行,同时jsp页面也不会执行。但是不会影响afterCompletion的执行
如果在控制器产生异常,则prehandle方法的执行不受任何影响,但是拦截器链中的所有postHandle不会执行,但是不会影响afterCompletion的执行
如果在拦截器1的prehandle中产生异常,则后续的拦截器prehandle都不会执行,控制器不会执行,afterCompletion不执行
如果在拦截器2的prehandle中产生异常,则前面的拦截器prehandle已经执行过了,控制器不执行,后续拦截器不执行,前面的afterCompletion会执行

拦截器常见用途:
1、日志记录:记录请求信息的日志
2、权限检查:如登录检查
3、性能检测:检测方法的执行时间

拦截器执行的时机
在服务器启动时完成拦截器对象的创建DispatcherServlet,运行采用单实例多线程的方式运行
1)preHandle():在请求被处理之前进行操作
2)postHandle():在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果
3)afterCompletion():所有的请求响应结束后执行善后工作,清理对象,关闭资源

SpringMVC运行机制
当用户发起请求后,执行DispacherServlet,如果是JSP直接调用jsp页面.如果不是JSP,DispacherServlet调用HandlerMapping判断请求URL是否合法,如果URL不存在报错404,如果URL存在使用HandlerAdapter调用具体的HandlerMethod,当Handler执行完成后会返回ModelAndView,会被ViewResovler解析,调用具体的物理视图.最终响应给客户端浏览器.

拦截器配置

<mvc:interceptors>
    <bean class="com.yan.intercepter.MyInter2"/>拦截全部
    <mvc:interceptor> 允许定义多个
         <mvc:mapping path="/**"/>配置拦截器作用的路径
         <bean class="com.yan.intercepter.MyInter">
            <property...>
         </bean>
    </mvc:interceptor>
</mvc:interceptors>

拦截器实现的两种方式
1)继承HandlerInterceptorAdapter的父类 extends HandlerInterceptorAdapter,@Deprecated
2)实现HandlerInterceptor接口,实现的接口,JDK1.8接口中实际上有默认实现,所以一般推荐使用实现接口的方式

业务需求1:登录状态检查

public class LoginCheckInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object obj = session.getAttribute("userInfo");
        if(obj!=null && obj instanceof User)
            return true;
        else{
//            response.sendError(403,"不能访问");
//            request.setAttribute("msg","需要登录才能使用");  如果需要使用request传递数据则必须请求转发
            response.sendRedirect(request.getContextPath()+"/user/login");
            return false;
        }
    }
}
 <mvc:interceptor>
        <mvc:mapping path="/admin/**"/>
        <bean class="com.yan.interceptors.LoginCheckInterceptor"/>
    </mvc:interceptor>

业务需求2:计算统计每次请求处理的时间
public class CounterInterceptor implements HandlerInterceptor {
//如何在preHandle和afterCompletion之间传递数据? 直接使用属性不行,因为多线程的运行方式。解决方案有可以使用ServletAPI对象传递数据,也可以使用ThreadLocal传递数据

  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Long start=System.currentTimeMillis();
        request.setAttribute("start",start);
        return true;
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        long start=(Long)request.getAttribute("start");
        long end=System.currentTimeMillis();
        String url=request.getRequestURL().toString();
        System.out.println("访问"+url+"用时为:"+(end-start)+"毫秒");
    }
}
<mvc:interceptors>  拦截器只有需要控制器处理的请求才有效
        <bean class="com.yan.interceptors.CounterInterceptor"/>
</mvc:interceptors>

HandlerInterceptor接口分析
Spring的HandlerMapping支持拦截器,拦截器必须实现HandlerInterceptor接口,此接口里面有下面3中方法:
1.preHandle()处理器执行前被调用,方法返回true标识继续调用其他拦截器或者处理器,返回false表示中断流程,后续的拦截器和处理器不再执行。一般请求下不能修改parameter
2.postHandle()处理器执行后,视图执行前调用,此时而已通过ModelAndView对象对数据模型数据进行处理或对视图进行处理。允许针对model和view进行修改
3.afterCompletion()整个过程结束后调用,比如性能监控中在这里可以记录结束时间并输出消耗的时间,也可以在这里写对资源的清理,但是只有preHandle()返回true时才会执行afterCompletion方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值