Administrator
发布于 2022-10-22 / 40 阅读
0
0

SpringMVC文件上传、拦截器

文件上传

导包

commons-fileupload-1.2.1.jar

commons-io-2.0.jar

写配置

1、单文件上传

<form action="upload" method="post" enctype="multipart/form-data">
    文件: <input type="file" name="file"/><br><br>
    描述: <input type="text" name="desc"/><br><br>
    <input type="submit" value="提交"/>
</form>

2、多文件上传

<form action="uploads" method="post" enctype="multipart/form-data">
    文件: <input type="file" name="files"/><br><br>
    文件: <input type="file" name="files"/><br><br>
    文件: <input type="file" name="files"/><br><br>
    文件: <input type="file" name="files"/><br><br>
    描述: <input type="text" name="desc"/><br><br>
    <input type="submit" value="提交"/>
</form>

3、控制器

 @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ResponseBody
    public String uploadFile(MultipartFile file) throws IOException {
     
        file.transferTo(new File("C:\\Users\\Admin\\Desktop\\Spring\\multipart\\"+file.getOriginalFilename()));
        System.out.println("Uploading......");
        return "success";

    }
--
多文件上传和单位文件上传的区别就是将入参改为数组形式就行
--
    @RequestMapping(value = "/uploads", method = RequestMethod.POST)
    @ResponseBody
    public String uploadFiles( MultipartFile[] files) throws IOException {

        for (MultipartFile file : files) {
            System.out.println(file.isEmpty());
            if (!file.isEmpty()){
                file.transferTo(new File("C:\\Users\\Admin\\Desktop\\Spring\\multipart\\"+file.getOriginalFilename()));
            }

        }


        System.out.println("Uploading files......");
        return "success";

    }

4、配置文件上传控制器

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="maxUploadSize" value="1024000"></property>
    </bean>

测试

唠叨几句

在写文件上传的时候,直接贴了人家的代码,然后里面有一句

InputStream inputStream = file.getInputStream();
导致我的程序报错
原因是我没有将这个流关掉,所以程序将文件上传完之后没办法将临时文件删掉
这估计是和inputStream不同的地方吧,虽然类型是inputStream,但不是说7以后会自动关嘛

然后就是什么?点进inputStream源码看了一眼,我只点了抽象类来看
还好奇他在读取流的时候是怎么知道上一次读取的位置,不然怎么配合while循环读取?
因为在抽象类里没有太多的具体实现。。也没有那样的下标参数,就在那里干想
直到我终于肯动手debug,然后进到了具体的实现类。。。。。

所以,补一句自己的发现,FileInputStream会调用底层的方法,
private native int readBytes(byte b[], int off, int len) throws IOException;
这个debug进不去,但ByteArrayInputStream可以,里面原来会定义一个常量值,保存上一次读取到的位置
那估计底层的方法也差不多吧

拦截器

概述

拦截器个人理解的话,就是高级的过滤器,毕竟可以对拦截的请求做一些处理,
DispatcherServlet也可以理解为一个拦截器,拦截所有请求
这里主要说自定义拦截器

自定义拦截器

用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口

拦截器的方法

preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。
如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,
则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。

postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,
在该方法中对用户请求request进行处理。

afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,
可以在该方法中进行一些资源清理的操作。

自定义拦截器

随便贴个简单的实现

public class MyFirstInterceptor implements HandlerInterceptor {
--
//请求方法之前的拦截处理
--
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("myfirst interceptor preHandle method running" );
        return true;
    }

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

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


----------------------------------------------------
public class MySecondInterceptor implements HandlerInterceptor  {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("mysecondInterceptor preHandle method running");
        return true;
    }

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

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

web.xml配置文件

可以在拦截器配置这里指定拦截那些请求eg:<mvc:mapping path="/xxx"/>

    <mvc:interceptors>
        <bean class="com.lly.Interceptor.MyFirstInterceptor"></bean>
        <bean class="com.lly.Interceptor.MySecondInterceptor"></bean>
    </mvc:interceptors>

运行

myfirst interceptor preHandle method running
mysecondInterceptor preHandle method running
处理hello请求....
mysecondInterceptor postHandle method running
myfirst interceptor postHandle method running
mysecondInterceptor afterCompletion method running
myfirst interceptor afterCompletion method running

拦截器的执行顺序按你配置文件的顺序执行

拦截器执行流程图

Image [47]

正常流程:

按配置顺序依次执行preHandle(),再执行目标方法的适配器和处理器;

然后再按一开始倒序来执行postHandle(),再渲染视图页面;

然后再倒序执行afterCompletion()

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

异常流程

已执行过preHandle()的会直接跳到执行afterCompletion()

拦截器原理

1、拦截器初始化

在容器加载bean组件的时候,会进行拦截器的初始化,且是在九大组件初始化之前
而拦截器是在容器加载RequestMappingHandlerMapping组件的时候,
加入到RequestMappingHandlerMapping中的


	@Override
	protected void initApplicationContext() throws BeansException {
		extendInterceptors(this.interceptors);
        --
        探查拦截器,并加入容器中
        --
		detectMappedInterceptors(this.adaptedInterceptors);
		initInterceptors();
	}

检测MappedInterceptor类型的bean,并将它们添加到映射拦截器列表中

  protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
      mappedInterceptors.addAll(
              BeanFactoryUtils.beansOfTypeIncludingAncestors(
                      obtainApplicationContext(), MappedInterceptor.class, true, false).values());
  }

2、拦截器的运行

拿到目标方法的处理器,处理器里有处理器执行链,里面就有拦截器等组件
mappedHandler = getHandler(processedRequest);

拦截器前置方法执行,如果拦截器返回false,方法直接return

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}

// Actually invoke the handler.
执行目标方法

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

3、applyPreHandle

  boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
          for (int i = 0; i < interceptors.length; i++) {
              HandlerInterceptor interceptor = interceptors[i];
              if (!interceptor.preHandle(request, response, this.handler)) {
                  triggerAfterCompletion(request, response, null);
                  return false;
              }
              --
              //记录下执行了的拦截器下标,方法运行结束后倒序执行
              --
              this.interceptorIndex = i;
          }
      }
      return true;
  }

4、applyPostHandle

mappedHandler.applyPostHandle(processedRequest, response, mv);

	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {

		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
        --
        //倒序执行拦截器的后置方法
        --
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}

5、triggerAfterCompletion

方法正常执行,拦截器的后续也会执行
    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
 ------------------------------------------------------------------------   
 
    方法执行和页面渲染都有两个大的catch,即使发生异常。triggerAfterCompletion都会执行
    
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  }
      catch (Exception ex) {
          triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
      }
      catch (Throwable err) {
          triggerAfterCompletion(processedRequest, response, mappedHandler,
                  new NestedServletException("Handler processing failed", err));
      }

拦截器执行流程总结

以配置的两个拦截器为例,看输出就能懂

正常执行

    myfirst interceptor preHandle method running
    mysecondInterceptor preHandle method running
    处理hello请求....
    mysecondInterceptor postHandle method running
    myfirst interceptor postHandle method running
    mysecondInterceptor afterCompletion method running
    myfirst interceptor afterCompletion method running
    
  第一个拦截器不给过后面都不执行
  
  第二个拦截器不给过
  myfirst interceptor preHandle method running
  mysecondInterceptor preHandle method running
  myfirst interceptor afterCompletion method running
  
  目标方法报错
  myfirst interceptor preHandle method running
  mysecondInterceptor preHandle method running
  处理hello请求....
  mysecondInterceptor afterCompletion method running
  myfirst interceptor afterCompletion method running
  十月 22, 2022 3:57:00 下午 org.apache.catalina.core.StandardWrapperValve invoke
  严重: Servlet.service() for servlet [dispatcher] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause
  java.lang.ArithmeticException: / by zero

评论