文件上传
原理
- 表单的enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。
- 当表单的enctype="application/x-www-form-urlencoded"(默认)时,form表单中的数据格式为:key=value&key=value
- 当表单的enctype="multipart/form-data"时,其传输数据形式如下
SpringBoot实现文件上传功能
- SpringBoot工程嵌入的tomcat限制了请求的文件大小,每个文件的配置最大为1Mb,单次请求的文件总数不能大于10Mb(这些限制是可修改的)
要更改这个默认值需要在配置文件(如application.properties)中加入两个配置
spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB
当表单的enctype="multipart/form-data"时,可以使用MultipartFile获取上传的文件数据,再通过transferTo方法将其写入到磁盘中
package com.example.demo.controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; @RestController public class FileController { /** * 该接口要求用户POST的时候携带两个参数,即用户名nickname和文件f * @param nickname * @param f * @return success */ @PostMapping("upload") public String upload(String nickname, MultipartFile f, HttpServletRequest request) throws IOException{ System.out.println("上传者:" + nickname); System.out.println("文件大小:" + f.getSize()); System.out.println("文件类型:" + f.getContentType()); System.out.println("原文件名:" + f.getOriginalFilename()); // request.getServletContext().getRealPath()方法获得一个tomcat服务器内的临时文件夹 // 再给getRealPath方法传入一个参数以在临时文件夹中创建一个新的文件夹 String path = request.getServletContext().getRealPath("/upload/"); System.out.println("存放路径:" + path); saveFile(f,path); return "上传成功"; } // 响应上传文件后的处理方法 public void saveFile(MultipartFile f, String path) throws IOException{ // 定义上传文件夹 File dir = new File(path); // 判断上传文件夹是否存在,不存在则自动创建 if(!dir.exists()){ dir.mkdir(); } // 上传后的路径与文件名 File file = new File(path + f.getOriginalFilename()); // 进行保存 f.transferTo(file); } }
文件上传之后,会默认放在tomcat服务器的临时文件夹下的路径,这个路径是随时变动的
通过这种方式上传的文件无法通过浏览器请求进行访问,如果需要浏览器请求访问,则需要修改配置文件
为上传的文件配置一个静态目录
spring.web.resources.static-locations=/upload/
因为这是个tomcat服务器下的临时路径,这个路径是一个系统上的路径,并不位于classpath下。故直接使用/路径
即可
可以看到成功访问到了上传的图片
拦截器
拦截器在Web系统中非常常见,对于某些全局统一的操作,我们可以把它提取到拦截器中实现。总结起来,拦截器大致有以下几种使用场景:
- 权限检查:如登录检测、进入处理程序检测是否登录,如果没有,则直接返回登录页面。
- 性能监控:有时系统在某段时间莫名其妙很慢,可以通过拦截器在进入处理程序之前开始记录时间,在处理完后记录结束时间,从而得到该请求的处理时间
- 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有提取Locale、Theme信息等,只要是多个处理程序都需要得,即可使用拦截器实现
SpringBoot定义了HandlerInterceptor接口来实现自定义拦截器的功能
HandlerInterceptor接口定义了preHandle、postHandle、afterCompletion三种方法,通过重写这三种方法实现请求前、后等操作
编写拦截器
在主包下创建一个包名为interceptor来存放相关的拦截器类
这里创建一个LoginInterceptor类来进行演示
package com.example.demo.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 拦截器需要实现HandlerInterceptor这个接口
public class LoginInterceptor implements HandlerInterceptor {
/**
*
* @param request HTTP请求
* @param response HTTP响应
* @param handler chosen handler to execute, for type and/or instance evaluation
* @return boolean
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("LoginInterceptor");
return true;
}
}
拦截器返回的是一个布尔值,只有该值为真时,才会将请求转交给下一个拦截器或者控制器
创建拦截器后,还需要进行注册才能生效
拦截器注册
写完拦截器后还需要在类中进行注册
- addPathPatterns方法定义拦截的地址
- excludePathPatterns定义排除某些地址不被拦截
- 添加的一个拦截器没有addPathPattern任何一个url则默认拦截所有请求
- 如果没有excludePathPatterns任何一个请求,则默认不放过任何一个请求
在主包下创建一个包名为config用于存放配置文件
新建一个WebConfig类来注册刚刚创建的拦截器
package com.example.demo.config;
import com.example.demo.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// 需要加上Configuration注解让SpringBoot读取这个类,这样下面的配置才能够生效
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 重写WebMvcConfigurer接口中增加拦截器的方法
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry){
/*
调用registry对象添加拦截器的方法,传入我们设置的拦截器,
并调用addPathPatterns指定拦截的路径
如果不指定拦截的路径,那么所有的资源都会被这个拦截器拦截处理
*/
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/user/**");
}
}
前面定义的拦截器中,对拦截到的路径的处理是打印LoginInterceptor
可以通过访问这个路径来看是否成功拦截到了请求
拦截器的注册类中定义的是拦截/user下的所有请求,尝试着访问
可以看到控制台成功打印出了LoginInterceptor,说明拦截器生效了
如果访问其他路径,控制台则不会打印,拦截器不生效