前言
Spring MVC为文件上传提供了良好的支持,而在Spring Boot中可以更加简便的配置文件上传所需的内容。为了更好的理解Spring Boot的配置,首先从SPring MVC的机制谈起
1. Spring MVC对文件上传的支持
首先,DispatcherServlet会使用适配器模式,将HttpServletRequest接口对象转换成MultiPartHttpServletRequest对象。MultipartHttpServletRequest接口扩展了HttpServletRequest接口所有的方法,而且定义了一些操作文件的方法,这样通过这些方法就可以实现对文件上传的操作。
下面探讨HttpServletRequest和MultipartHttpServletRequest的关系,如图所示
这里对于文件上传的场景,Spring MVC会将HttpServletRequest对象转换为MultipartHttpServletRequest对象。从MultipartHttpServletRequest接口可以看出,它存在很多方法来处理文件,这样在Spring MVC中操作文件就非常便捷了。
只是使用Spring MVC上传文件时,还需要配置MultipartHttpServletRequest,这个任务是通过MultipartResolver接口实现的。对于MultipartResolver接口,他又存在两个实现按类,这两个实现类分别是StandardServletMultipartResolver和CommonMultipartResolver,可以使用他们中的任意一个来实现文件上传。
在默认情况腺癌Spring推荐使用的是StandardServletMultipartResolver,因为他只需要依赖Servlet API提供的包,而对于CommonMultipartResolver,则需要依赖Apache提供的第三方包来实现,这显然没有StandardServletMultipartResolver来的实在。从实用的角度来说,因为Spring 3.1之后就已经能够支持StandardServletMultipartREsolver,所以CommonMultipartResolver已经被渐渐废弃了,这里不再对其进行介绍。他们的关系图如下:
在Spring Boot机制中,如果你没定义MultipartResolver对象,那么自动配置的机制会为你自动创建MultipartResolver对象,实际为StandardServletMultipartResolver,所以你并不需要自己去创建他.为了更加灵活,Spring Boot提供一下代码所示的配置项目
spring:
servlet:
multipart:
#是否启用多分部文件上传功能
enabled: true
#将文件写入磁盘的阙值
file-size-threshold: 0
max-file-size: 100000000 #限制单个文件大小
#限制所有文件大小
max-request-size: 100000000
#是否延迟多部件文件请求的参数和文件的解析
resolve-lazily: false
根据这些配置Spring Boot会自动生成StandardServletMultipartResolver对象,这样就可以对上传的文件进行配置.对于文件的上传可以使用Servlet API提供的Part接口.也可以使用Spring MVC提供的MulyipartFile接口作为参数.其实无论使用哪一个类都是允许的,只是我更加推荐使用Part,因为毕竟MultipartFile是Spring MVC提供的第三方才能够进行支持的,后续版本变化的几率更大一些.
2. 开发文件上传功能.
开发Spring Boot下的MVC上传,首先需要配置代码清单中的配置项
spring:
servlet:
multipart:
#是否启用多分部文件上传功能
enabled: true
#将文件写入磁盘的阙值
file-size-threshold: 0
max-file-size: 100000000 #限制单个文件大小
#限制所有文件大小
max-request-size: 100000000
#是否延迟多部件文件请求的参数和文件的解析
resolve-lazily: false
这里不指定上传的文件夹,因为在实际开发中还是直接放到项目目录之中.单个文件大小爱哦100MB,所有文件大小最大100MB.为了测试文件上传需要编写HTML文件.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" http-equiv="Content-Type" content="text/html">
<title>文件上传</title>
<script src="https://code.jquery.com/jquery-3.2.0.js"></script>
</head>
<body>
<form method="post" action="./request" enctype="multipart/form-data">
<input type="file" name="file" value="请选择上传文件">
<input type="submit" value="提交">
</form>
</body>
</html>
请注意,这里的form表单声明为multipart/form-data,如果没有这个声明,Spring MVC就会解析文件出错,从而导致上传文件失败.有了HTML文件,下面开发文件上传控制器(这个控制器将包括使用HttpServletRequest, MultipartFile和Part参数)来完成文件上传.代码清单如下:
package cn.hctech2006.boot.bootmvc.controller;
@Controller
@RequestMapping("/file")
public class FileController {
/**
* 打开文件上传请求页面
* @return
*/
@GetMapping("/upload/page")
public String uploadPage(){
return "/role/upload";
}
@PostMapping("/upload/request")
@ResponseBody
public Map<String, Object> uploadRequest(HttpServletRequest request) throws FileNotFoundException {
boolean flag = false;
MultipartHttpServletRequest mreq = null;
if(request instanceof MultipartHttpServletRequest){
mreq = (MultipartHttpServletRequest) request;
}else{
return dealResultMap(false,"文件上传失败");
}
MultipartFile mf = mreq.getFile("file");
String fileName = mf.getOriginalFilename();
String url = ResourceUtils.getURL("").getPath()+fileName;
File file = new File(url);
try{
mf.transferTo(file);
return dealResultMap(true, "上传成功");
}catch (Exception e){
e.printStackTrace();
return dealResultMap(false, "上传失败");
}
}
@PostMapping("/upload/multipart")
@ResponseBody
public Map<String, Object> uploadMultipart(MultipartFile file) throws FileNotFoundException {
boolean flag = false;
String fileName = file.getOriginalFilename();
String url = ResourceUtils.getURL("").getPath()+fileName;
File folder = new File(url);
try{
file.transferTo(folder);
return dealResultMap(true, "上传成功");
}catch (Exception e){
e.printStackTrace();
return dealResultMap(false, "上传失败");
}
}
@PostMapping("/upload/part")
@ResponseBody
public Map<String, Object> uploadPart(Part file) throws FileNotFoundException {
boolean flag = false;
String fileName = file.getSubmittedFileName();
String url = ResourceUtils.getURL("").getPath()+fileName;
try{
file.write(url);
return dealResultMap(true, "上传成功");
}catch (Exception e){
e.printStackTrace();
return dealResultMap(false, "上传失败");
}
}
private Map<String, Object> dealResultMap(boolean success, String msg){
Map<String, Object> result = new HashMap<>();
result.put("success",success);
result.put("msg", msg);
return result;
}
}
代码中的uploadPage方法用来映射上传文件的HTML,所以只需要请求他便能够打开上传文件的页面.uploadRequest方法则将HttpServletRequest对象传递.
从之前的分析可以知道,在调用控制器之前,DispatcherServlet会将器转换为MultipartHttpServletRequest对象,所以方法中使用了强制转换,从而得到MultipartHttpServletRequest对象,然后获取Multipart对象,截止使用MultipartFile对象的getOriginalFileName方法就可以得到上传的文件名,而通过他的transferTo方法,就可以将文件保存到对应的路径之中.
uploadMultipartFIle则是直接使用MultipartFile对象获取上传的文件,从而进行操作,uploadPart方法则是使用Servlet放入API,可以使用write方法直接写入文件,这也是推荐的.