SpringMVC4-文件上传下载-拦截器
1 文件下载
通过ResponseEntity封装以下三组信息返回下载
- 数据缓存数组:bytes数组
- 响应头信息:HttpHeader,内容填充格式固定
- 响应码信息:HttpStatus,例如404
/**
* @author : Lcywings
* @date : 2021/6/29 8:42
* @acl : true
* @description : 图片下载
* 下载原理:将服务器端的文件以流的形式写到客户端,通过浏览器保存实现下载
*/
@RequestMapping("/downloadPic")
public ResponseEntity<byte[]> downloadPic(HttpSession session) throws IOException {
// 字节缓冲数组
byte[] bytes = null;
// 读取服务器上的图片,借助session获取ServletContext对象,有一个将文件读成流的方法
InputStream inputStream = session.getServletContext().getResourceAsStream("pic/girl.jpg");
// 定义缓存数组大小,目标文件多大,创建多大的缓存
bytes = new byte[inputStream.available()];
// 将目标图片文件,全部读取到缓存数组中
inputStream.read(bytes);
// 使用ResponseEntity封装相应数据
// 封装的内容: 1 发送给客户端的响应数据 2 响应头信息 3 响应码状态
// 响应头信息
HttpHeaders headers = new HttpHeaders();
// 下面是固定写法,不能乱写,attachment代表附件,filename提示下载的文件名,可以随机或者自定义
headers.add("Content-Disposition", "attachment;filename=girl.jpg");
// 设置响应码
HttpStatus status = HttpStatus.OK;
// 封装信息
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, status);
//返回封装信息
return responseEntity;
}
2 文件上传
文件上传的三板斧
- 获取上传文件名,及其需要创建文件存储目录
- 根据上面俩条创建目标文件
- 上传文件
/**
* @author : Lcywings
* @date : 2021/6/29 9:41
* @acl : true
* @description : 文件上传
*/
@RequestMapping(value = "uploadPic", method = RequestMethod.POST)
public String uploadPic(@RequestParam("uploadFile") MultipartFile multipartFile,
HttpSession session, Map<String, Object> map) throws IOException {
// 获取到上传文件的名称
String fileName = multipartFile.getOriginalFilename();
// 获取服务器上传文件的真实路径(还可以指定服务器的固定目录)
String realPath = session.getServletContext().getRealPath("upload");
System.out.println(realPath);
// 创建文件对象
File file = new File(realPath);
// 判断目录是否存在,如果不存在,就创建目标目录,如果存在就直接使用
if (!file.exists()) {
if (file.mkdirs()) {
System.out.println("目标目录不存在,创建成功!");
} else {
System.out.println("目标目录不存在,创建失败!");
}
}
// 创建一个目标文件对象
File targeFile = new File(realPath + "/" + fileName);
// 基础写法
// 获取源文件的输入流对象
// InputStream inputStream = multipartFile.getInputStream();
//
// //创建输出流对象
// FileOutputStream fos = new FileOutputStream(targeFile);
//
// // 循环读取
// int hasRead;
// while ((hasRead = inputStream.read()) != -1) {
// fos.write(hasRead);
// }
// fos.flush();
// inputStream.close();
// fos.close();
// 高级用法
multipartFile.transferTo(targeFile);
// 将文件名返回页面,方便显示上传的头像
map.put("fileName", fileName);
// 不走视图解析
return "forward:load.jsp";
}
主要作用是为了使用即插即用的 MultipartResolver,搞到对象
<!-- 文件上传组件 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 保证字符集编码必须跟Jsp页面使用的编码一致 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件的大小限制 -->
<property name="maxInMemorySize" value="10485760"/>
</bean>
拦截器
拦截器与过滤器的区别:
- 过滤器:在请求到达servlet之前过滤请求,比如:字符集编码过滤器,免登陆过滤器,rest风格过滤器,安全校验过滤器等
- 拦截器:在请求到达servlet中执行,拦截非法请求,比如:敏感字拦截,非法请求拦截等
拦截器生效需要的配置:
<!-- 让拦截器生效,必须增加配置,加入拦截器 -->
<mvc:interceptors>
<ref bean="myInterceptor"/>
</mvc:interceptors>
自定义拦截器:
-
实现 HandlerInterceptor 接口
-
继承 HandlerInterceptorAdapter 适配器类
需要重写三个方法:
- preHandle()
- postHandle()
- afterCompletion()
package com.kgc.springmvc.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Created on 2021-06-29.
* <p>
* Author: zhukang
* <p>
* Description: 自定义拦截器
*
* 拦截器和过滤器的区别:
* 过滤器:在请求到达servlet之前过滤请求,比如:字符集编码过滤器,免登陆过滤器,rest风格过滤器,安全校验过滤器等
* 拦截器:在请求到达servlet中执行,拦截非法请求,比如:敏感字拦截,非法请求拦截等
* 总结一点:过滤器早于拦截器执行
*
* 拦截器的实现方式:
* 1)实现 HandlerInterceptor 接口 2)继承 HandlerInterceptorAdapter 适配器类
*/
@Component
public class MyInterceptor implements HandlerInterceptor {
/**
* 执行时机:在调用目标请求处理器的请求处理方法之前执行,在DispatcherServlet的962行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("------ MyInterceptor preHandle ... ------");
// true:代表请求允许访问,不需要拦截,将请求交给后续的拦截器或者目标请求处理器中的处理方法
// false:代表请求不允许访问,需要拦截,请求就不会交给后续的拦截器或者目标请求处理器中的处理方法
return true;
}
/**
* 执行时机:在目标请求处理器的请求处理方法执行后,在模型属性渲染之前调用,在DispatcherServlet的974行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("------ MyInterceptor postHandle ... ------");
}
/**
* 执行时机:
* 正常:没有发生异常,在视图处理(渲染模型数据,转发请求等)后,调用,在1059行,从最后一个拦截器开始依次往前执行此方法
* 产生异常,调用triggerAfterCompletion方法,在987和990行
*
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("------ MyInterceptor afterCompletion ... ------");
}
}
多个拦截器:
→ | → | → | → | → |
↓这个拦截为false,则直接从上一个拦截器看是执行afterCompletion方法,
← | ← | ← postHandle则不执行
package com.kgc.springmvc.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Created on 2021-06-29.
* <p>
* Author: zhukang
* <p>
* Description: 自定义拦截器2
* 多个拦截器,执行次序:
* preHandle:正向执行的,跟核心配置文件中,配置的拦截器的顺序有关
* postHandle:逆向执行的,由于都是先走完所有的拦截器的preHandle方法,如果有一个拦截器的preHandle方法返回false,所有的拦截器的postHandle方法就不会执行
* afterCompletion:逆向执行的,从当前拦截器之前的那个拦截器逆序依次执行
*/
@Component
public class MyInterceptor2 implements HandlerInterceptor {
/**
* 执行时机:在调用目标请求处理器的请求处理方法之前执行,在DispatcherServlet的962行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("------ MyInterceptor2 preHandle ... ------");
// true:代表请求允许访问,不需要拦截,将请求交给后续的拦截器或者目标请求处理器中的处理方法
// false:代表请求不允许访问,需要拦截,请求就不会交给后续的拦截器或者目标请求处理器中的处理方法
return false;
}
/**
* 执行时机:在目标请求处理器的请求处理方法执行后,在模型属性渲染之前调用,在DispatcherServlet的974行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("------ MyInterceptor2 postHandle ... ------");
}
/**
* 执行时机:
* 正常:没有发生异常,在视图处理(渲染模型数据,转发请求等)后,调用,在1059行,从最后一个拦截器开始依次往前执行此方法
* 产生异常,调用triggerAfterCompletion方法,在987和990行
*
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("------ MyInterceptor2 afterCompletion ... ------");
}
}
释放静态资源的问题解决
<!-- springmvc默认是不会释放静态资源(js,css,image,html...),即被前端核心控制器拦截(找映射处理方法)
针对静态资源要进行释放,增加一个配置<mvc:default-servlet-handler/>即可
注意:静态资源释放会导致@RequestMapping注解失效,必须配置注解扫描使用
-->
<mvc:default-servlet-handler/>
<!--
作用:
1)配置了<mvc:view-controller/>
2)配置了<mvc:default-servlet-handler/>
3)处理json数据返回,默认装配的对象:RequestMappingHandlerAdapter,默认对HttpMessageConverter提供支持
原理:
比如随着spring版本升级,老的版本不够完善,在后续版本中需要提供新的组件来代替
启动新的组件代替旧的组件,实现新的强大的功能
-->
<mvc:annotation-driven/>
SpringIOC容器 & SpringMVCIOC容器
SpringMVC 的 IOC 容器中的 bean 可以来引用 Spring IOC 容器中的 bean. 返回来呢 ? 反之则不行. Spring IOC 容器中的 bean 却不能来引用 SpringMVC IOC 容 器中的 bean 。
- 在 Spring MVC 配置文件中引用业务层的 Bean
- 多个 Spring IOC 容器之间可以设置为父子关系,以实现良好的解耦。
- Spring MVC WEB 层容器可作为 “业务层” Spring 容器的子容器: 即 WEB 层容器可以引用业务层容器的 Bean,而业务层容器却访问不到 WEB 层容 器的 Bean
Spring 工作流程描述
1) 用户向服务器发送请求,请求被 SpringMVC 前端控制器 DispatcherServlet 捕获;
2) DispatcherServlet 对请求 URL 进行解析,得到请求资源标识符(URI):
判断请求 URI 对应的映射
1 不存在:
- 再判断是否配置了 mvc:default-servlet-handler:
- 如果没配置,则控制台报映射查找不到,客户端展示 404 错误
- 如果有配置,则执行目标资源(一般为静态资源,如:JS,CSS,HTML)
2 存在:
- 执行下面流程
3) 根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象 的形式返回;
4) DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter。
5) 如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(...)方法【正 向】
6) 提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler(Controller)方法, 处理请求。在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外 的工作:
- HttpMessageConveter:将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息
- 数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等
- 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult 或 Error 中
7) Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;
8) 此时将开始执行拦截器的 postHandle(...)方法【逆向】
9) 根据返回的 ModelAndView(此时会判断是否存在异常:如果存在异常,则执行 HandlerExceptionResolver 进行异常处理)选择一个适合的 ViewResolver(必须是已 经注册到Spring容器中的ViewResolver)返回给DispatcherServlet,根据Model和View, 来渲染视图
10) 在返回给客户端时需要执行拦截器的 AfterCompletion 方法【逆向】
11) 将渲染结果返回给客户端
Q.E.D.