来个例子
客户端
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleInputEmail1">邮箱</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1"
placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">名字</label>
<input type="text" name="userName" class="form-control" id="exampleInputPassword1"
placeholder="Password">
</div>
<div class="form-group">
<label for="headImage">头像</label>
<input type="file" name="headImage" id="headImage">
<p class="help-block">Example block-level help text here.</p>
</div>
<div class="form-group">
<label for="photos">File input</label>
<input type="file" name="photos" id="photos" multiple>
<p class="help-block">Example block-level help text here.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
服务端
@Log4j2
@Controller
public class UpLoadController {
@PostMapping("/upload")
public String upLoad(@RequestParam("email") String email,
@RequestParam("userName") String userName,
@RequestPart("headImage") MultipartFile headImage,
@RequestPart("photos") MultipartFile[] photos) {
log.info("上传信息:email:{},userName:{},headImage:{},photos:{}", email, userName, headImage.getSize(), photos.length);
return "form/form_layouts";
}
}
设置单个文件大小限制、总体文件大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-file-size=100MB
原理
在SpringBoot启动的时候,会通过MultipartAutoConfiguration向IOC中注入一个文件解析器:StandardServletMultipartResolver,就是它负责判断和解析的
判断当前的请求是否是文件上传
请求来到DispatcherServlet中的doDispatch方法,然后在调用checkMultipart方法实现判断:就是判断当前的请示是否以"multipart/"开头,所有以我们在请求端的时候标注的enctype="multipart/form-data"是很重要的。
@Override
public boolean isMultipart(HttpServletRequest request) {
return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}
然后调用StandardServletMultipartResolver的resolveMultipart方法将请求封装成MultipartHttpServletRequest返回
根据客户端的参数和服务端接收的参数找到对应的参数解析器
得到MultipartHttpServletRequest之后,会去HandlerMapping中获取对应的HandlerExecutionChain(里面包含的了HandlerAdapter和拦截器),在调用HandlerAdapter的handle( )方法获取参数解析器。匹配对应的参数解析器的代在:HandlerMethodArgumentResolverComposite类中的getArgumentResolver( )方法,最后得到的解析文件上传的解析器叫做RequestPartMethodArgumentResolver,判断方法就是看你参数上是否标注了RequestPart注解
//拿到系统内置的参数解析器,用单个参数挨个的匹配使用哪个文件解析器(第一次查找到之后会缓存起来)
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
解析文件
最后使用的是MultipartResolutionDelegate类中的静态方法resolveMultipartArgument获取到客户端上传的文件
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
......省略很多.....
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
......省略很多.....
}