SpringMVC高级
课程计划
-
文件上传
-
文件下载
-
异常处理
-
拦截器
-
参数类型转换
学习目标
-
MVC的方式上传文件
-
流的形式下载文件
-
自定义异常的处理
-
拦截器的原理和案例
八、MVC文件上传
1、引入依赖
-
引入commons-io:是为了简化文件保存、流处理等操作,提示开发效率。
-
里面提供了大量的工具类(FileUtils,IOUtils),简化了文件的读写、流处理、路径操作等常见任务
<!--文件工具--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.15.1</version> </dependency>
2、修改配置文件
public class WebInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//创建AnnotationConfigWebApplicationContext对象。声明启动类
AnnotationConfigWebApplicationContext context =
new AnnotationConfigWebApplicationContext();
context.register(WebMvcConfig.class);
//注册 DispatcherServlet
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration =
servletContext.addServlet("dispatcher",dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
//注册 字符编码 过滤器
FilterRegistration.Dynamic encodingFilter =
servletContext.addFilter("characterEncodingFilter", CharacterEncodingFilter.class);
encodingFilter.setInitParameter("encoding","UTF-8");
encodingFilter.setInitParameter("forceEncoding","true");
encodingFilter.addMappingForUrlPatterns(null,false,"/");
//注册 文件上传参数
//配置临时文件
String tempDir = System.getProperty("java.io.tmpdir");//系统临时文件夹
//配置:允许上传的最大文件大小,临时文件阈值
MultipartConfigElement multipartConfig = new MultipartConfigElement(
tempDir, //临时文件目录
1024*1024*10, //单个文件最大大小:10MB
1024*1024*50, //整个请求最大大小:50MB
1024*1024 //超过此大小(1MB)的文件会先存储到临时目录
);
//文件的配置,绑定到DispatcherServlet中
registration.setMultipartConfig(multipartConfig);
}
}
3、单文件上传
@RestController
@RequestMapping("/file")
public class UploadController {
@Autowired
HttpServletRequest request;
//写一个方法,接收页面传入的文件
@PostMapping("/doUpload1")
public String doUpload1(MultipartFile abc) throws IOException {
//获取文件保存的根路径--得到文件之后,保存到哪里
String basePath = getBasePath();
//获取文件名/生成文件名
String fileName = abc.getOriginalFilename();
//保存文件
abc.transferTo(new File(basePath + fileName));
return "basePath + fileName";
}
private String getBasePath(){
//获取项目的根路径,把上传来的图片/文件 存储到这里
String realPath = request.getServletContext().getRealPath("/");
// 对应的是webapp的根路径
System.out.println(realPath);
String savePath = realPath + "imgs/";
//判断文件夹是否存在
File baseFile = new File(savePath);
if (!baseFile.exists()){
baseFile.mkdirs();
}
return savePath;
}
}

4、多文件上传
@PostMapping("/doUpload2")
public String doUpload2(@RequestParam("abc") List<MultipartFile> files) throws IOException {
if (files!=null && !files.isEmpty()){
for (MultipartFile file : files){
file.transferTo(new File(getBasePath()+file.getOriginalFilename()));
}
}
return "success";
}

注意:测试的时候,注意被选中文件的大小,如果超过10MB,上传失败
九、文件下载
1、配置静态资源文件放行
@Configuration
@ComponentScan(basePackages = "com.javasm")
@EnableWebMvc//开启WebMvc
public class WebMvcConfig implements WebMvcConfigurer {
//在下方,添加各种MVC配置
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/imgs/**")//浏览器访问的前缀
.addResourceLocations("/imgs/");//资源在项目中的实际存储路径(相较于根路径)
registry.addResourceHandler("/save/**")
.addResourceLocations("/save/");
}
}
http://127.0.0.1:8080/mvc/imgs/pic_2.jpg 访问这个路径,可以直接下载文件 如果浏览器支持当前下载的文件,会直接打开,不触发下载。 如果浏览器不支持当前文件,会触发下载。 这种下载方式,最简单,最便捷,但是也有缺点。

2、流下载
这个下载方式,必须经过服务器,让服务器写出文件,写出流信息到浏览器中,百分百触发下载
2.1 ResponseEntity
//下载
@GetMapping("/t1/{name}")
public ResponseEntity<byte[]> test1(@PathVariable String name) throws Exception {
//获取文件根路径
String realPath = request.getServletContext().getRealPath("/save/");
//文件全路径
String allPath = realPath + name;
//获取下载需要的文件信息
FileInputStream inputStream = new FileInputStream(allPath);
//获取 流的数组
byte[] buffer = new byte[inputStream.available()];
//读取== 把流 读取到数组里
inputStream.read(buffer);
//配置 响应的头信息
//文件下载的时候,需要显示的文件名
HttpHeaders headers = new HttpHeaders();
//设置防止中文文件名乱码
headers.setContentDispositionFormData("attachment", URLEncoder.encode(name,"utf-8"));
//new 返回的对象
return new ResponseEntity<>(buffer, headers, HttpStatus.OK);
}
http://localhost:8080/mvc/file/t1/t2.pdf
2.2 HttpServletResponse
@GetMapping("/test2/{name}")
private void test2(@PathVariable String name) throws Exception{
String realPath = request.getServletContext().getRealPath("/save/");
String allPath = realPath + name;
File file = new File(allPath);
if (!file.exists()){
throw new FileNotFoundException(name+"文件不存在");
}
//代码运行到这里,说明文件是正常存在的
String encodeFileName = URLEncoder.encode(name, StandardCharsets.UTF_8);
//设置头信息--比如容易出错,注意查看单词拼写和标点符号
response.setHeader("Content-Disposition","attachment;filename*=UTF-8''"+encodeFileName);
//try……with……resource 自动关闭资源
//FileInputStream inputStream = new FileInputStream(file) 读取 文件的 流信息
//response.getOutputStream() 获取response对象中的向浏览器写的 流对象
try(FileInputStream inputStream = new FileInputStream(file);
OutputStream outputStream = response.getOutputStream()) {
//把读取到的流信息,写出到浏览器中
IOUtils.copy(inputStream,outputStream);
outputStream.flush();
}catch (IOException e){
throw new RuntimeException(e);
}
}
http://localhost:8080/mvc/file/test2/t2.pdf
十、异常
1、RestControllerAdvice注解
异常的知识点是非常重要的,在后续的SpringBoot项目中,依然在使用
//当前类,是当前项目,全局的异常处理类
//当产生异常的时候,触发类中的方法
@RestControllerAdvice
public class JavasmExceptionAdvice {
//你想让异常产生的时候,返回给用户什么内容,这里就配置什么类型的返回值
//当前的方法,是处理哪种异常的
//当前项目产生的所有ArithmeticException,都会进入这个方法进行处理
@ExceptionHandler(ArithmeticException.class)
public String f1(ArithmeticException e){
//打印异常,防止出现异常之后,返回了数据,但是程序员不知道哪行代码产生的异常
e.printStackTrace();
return e.getMessage();
}
}
2、自定义异常
-
自定义异常
//当程序中,产生了错误提示的时候,可以抛出自定义的异常
//主要是处理业务逻辑中,不合理的内容
public class JavasmException extends RuntimeException{
public JavasmException() {
}
public JavasmException(String message){
super(message);
}
}
-
Controller使用
@GetMapping("/t10")
public String test10(Integer id){
if (id ==10){
throw new JavasmException("id不能是10");
}
int i = 10/id;
return "Hello Test10";
}
-
修改全局异常
//当前类,是当前项目,全局的异常处理类
//当产生异常的时候,触发类中的方法
@RestControllerAdvice
public class JavasmExceptionAdvice {
//你想让异常产生的时候,返回给用户什么内容,这里就配置什么类型的返回值
//当前的方法,是处理哪种异常的
//当前项目产生的所有ArithmeticException,都会进入这个方法进行处理
@ExceptionHandler(ArithmeticException.class)
public String f1(ArithmeticException e){
//打印异常,防止出现异常之后,返回了数据,但是程序员不知道哪行代码产生的异常
e.printStackTrace();
return e.getMessage();
}
@ExceptionHandler(JavasmException.class)
public Map<String,Object> f2(JavasmException e){
Map<String,Object> map = new HashMap<>();
map.put("code",500);
map.put("msg",e.getMessage());
return map;
}
//处理哪些没有精准匹配的异常
@ExceptionHandler(Exception.class)
public Map<String,Object> f3(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("code",900);
map.put("msg",e.getMessage());
e.printStackTrace();
return map;
}
}
十一、拦截器
-
拦截器和过滤器的区别是什么?
-
过滤器:是Web容器,不属于框架,请求进入Tomcat之后,先经过过滤器
-
拦截器:属于Spring框架,SpringWeb中,相当于Servlet内部的请求拦截
-

测试案例
-
拦截器Java类
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("---preHandler--在方法执行之前被执行");
//返回值 必须是Boolean类型,如果返回true,允许进入下一站
//如果返回false,说明没有通过验证,不让继续访问
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandler------方法执行之后被执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion------整个请求结束之后,执行的方法,渲染之后执行");
}
}
-
WebMvcConfig配置类,添加拦截器
@Configuration
@ComponentScan(basePackages = "com.javasm")
@EnableWebMvc//开启WebMvc
public class WebMvcConfig implements WebMvcConfigurer {
//在下方,添加各种MVC配置
//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor())
//指定拦截器,拦截的路径,
.addPathPatterns("/**")// /**表示拦截所有的路径
.excludePathPatterns("/login","/imgs/**");
//如果有多个拦截器,在下面继续配置
}
//配置放过静态资源文件
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/imgs/**")//浏览器访问的前缀
.addResourceLocations("/imgs/");//资源在项目中的实际存储路径(相较于根路径)
registry.addResourceHandler("/save/**")
.addResourceLocations("/save/");
}
}
登录拦截器
-
拦截器Java类
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//只有登录的用户,才能访问Controller
Object user = request.getSession().getAttribute("user");
if (user != null){
return true;
}
//代码执行到这里,说明没有登录
Map<String,Object> map = new HashMap<>();
map.put("code",1001);
map.put("msg","用户未登录,禁止访问");
String s = JSON.toJSONString(map);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(s);
response.getWriter().close();
return false;
}
}
-
配置类
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/doLogin");
十二、数据转换
1、普通参数
"Method parameter 'ctime': Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; Failed to convert from type [java.lang.String] to type [java.util.Date] for value [2025-11-18]
@PostMapping("/doAdd1")
public String doAdd1(String uname, Date ctime){
return uname;
}
@PostMapping("/doAdd1")
public String doAdd1(String uname,@DateTimeFormat(pattern = "yyyy-MM-dd") Date ctime){
return uname;
}
2、实体类参数
Validation failed for argument [0] in public com.javasm.mvcdemo.webuser.model.WebUser com.javasm.mvcdemo.webuser.controller.LoginController.doAdd3(com.javasm.mvcdemo.webuser.model.WebUser): [Field error in object 'webUser' on field 'ctime': rejected value [2025-11-18]; codes [typeMismatch.webUser.ctime,typeMismatch.ctime,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [webUser.ctime,ctime]; arguments []; default message [ctime]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'ctime'; Failed to convert from type [java.lang.String] to type [java.util.Date] for value [2025-11-18]]]
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WebUser {
private Integer uid;
private String username;
private Company company;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date utime;
private Date ctime;
}
@PostMapping("/doAdd3")
public WebUser doAdd3(WebUser webUser){
return webUser;
}
3、自定义参数类型转换器
写一个转换类,做全局的时间处理
public class StringToDate implements Converter<String, Date> {
//支持的日志格式,提前写出来
private static final String[] PATTERNS ={
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyy-MM-dd",
"yyyy-MM-dd'T'HH:mm",
"yyyy/MM/dd HH:mm:ss",
"yyyy/MM/dd"
};
@Override
public Date convert(String string) {
if (string ==null || string.trim().isEmpty()){
return null;
}
for (String pattern : PATTERNS){
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.CHINA);
return sdf.parse(string.trim());//转换成功返回
} catch (ParseException e) {
//解析失败是正常的
continue;
}
}
//如果代码执行到这里了
throw new IllegalArgumentException("无法解析日期格式"+string+
",目前仅支持格式"+String.join(",",PATTERNS));
}
}
-
修改配置类WebMvcConfig
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToDate());
}
-
DateTime
public class StringToDateTime implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
return LocalDateTime.parse(source, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToDate());
registry.addConverter(new StringToDateTime());
}
4、JSON 数据转换
-
引入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.16.1</version><!-- 与 Spring6 兼容-->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.2</version>
</dependency>
-
修改实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WebUser {
private Integer uid;
private String username;
private Company company;
//@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date utime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date ctime;
//@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime addTime;
}
{
"uid":"1001",
"username":"admin",
"ctime":"2023-09-09 12:12:12",
"utime":"2023-09-09 12:12:12"
}
第二天总结
重点
-
文件上传
-
流下载
-
@RestControllerAdvice
-
拦截器
783

被折叠的 条评论
为什么被折叠?



