@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
log.info("上传的信息:email={},username={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
if(!headerImg.isEmpty()){
//保存到文件服务器,OSS服务器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:\cache\"+originalFilename));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\cache\"+originalFilename));
}
}
}
return "main";
}
配置文件修改:
#文件上传的相关配置,第一个是单个文件最大大小,第二个是总的文件最大大小 spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=100MB三、自动配置原理
文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties配置属性类
//MultipartAutoConfiguration类中
@ConditionalOnMissingBean(MultipartResolver.class)
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
自动配置好了 StandardServletMultipartResolver 【文件上传解析器(判断是否是文件请求)】
文件上传原理:
1、请求进来 使用文件上传解析器判断(checkMultipart中的isMultipart方法判断)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
//doDispatch方法中,先判断是否是文件请求 processedRequest = this.checkMultipart(request); //通过请求是否被包装,确定是否是文件上传请求 multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest);
文件上传解析器multipartResolver只有一个StandardServletMultipartResolver :
checkMultipart的具体代码:
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
//判断是否是文件请求
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
this.logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
}
} else if (this.hasMultipartException(request)) {
this.logger.debug("Multipart resolution previously failed for current request - skipping re-resolution for undisturbed error rendering");
} else {
try {
//封装文件请求,变为MultipartHttpServletRequest请求类型
return this.multipartResolver.resolveMultipart(request);
} catch (MultipartException var3) {
if (request.getAttribute("javax.servlet.error.exception") == null) {
throw var3;
}
}
this.logger.debug("Multipart resolution failed for error dispatch", var3);
}
}
this.multipartResolver.isMultipart(request):判断是否是文件请求
return this.multipartResolver.resolveMultipart(request);封装文件请求为请求类型MultipartHttpServletRequest,具体的是StandardMultipartHttpServletRequest。
2、参数解析器来解析请求中的文件内容封装成MultipartFile
最终选择RequestPartMethodArgumentResolver参数解析器解析,
RequestPartMethodArgumentResolver调用resolveArgument方法,如下是方法中的关键代码:
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
而上面这个resolveMultipartArgument方法关键代码如下:
//如果参数是文件数组,从请求的Map中 拿取请求的文件 赋给请求参数 parts = ((MultipartHttpServletRequest)multipartRequest).getFiles(name);
3、StandardServletMultipartResolver 将request中的文件信息封装为一个Map。
StandardServletMultipartResolver 将普通request包装为MultipartHttpServletRequest(接口),更具体的是StandardMultipartHttpServletRequest类
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
StandardMultipartHttpServletRequest的parseRequest方法中将request中文件信息封装为一个Map,上述的getFiles方法就是从Map中拿取对应的文件
//AbstractMultipartHttpServletRequest类 public ListgetFiles(String name) { List multipartFiles = (List)this.getMultipartFiles().get(name); return multipartFiles != null ? multipartFiles : Collections.emptyList(); }
getMultipartFiles()会返回一个Map,MultiValueMap
protected MultiValueMapgetMultipartFiles() { if (this.multipartFiles == null) { this.initializeMultipart(); } return this.multipartFiles; }
//StandardMultipartHttpServletRequest类 上面代码的initializeMultipart具体实现
private void parseRequest(HttpServletRequest request) {
try {
Collection parts = request.getParts();
this.multipartParameterNames = new linkedHashSet(parts.size());
MultiValueMap files = new linkedMultiValueMap(parts.size());
Iterator var4 = parts.iterator();
while(var4.hasNext()) {
Part part = (Part)var4.next();
String headerValue = part.getHeader("Content-Disposition");
ContentDisposition disposition = ContentDisposition.parse(headerValue);
String filename = disposition.getFilename();
if (filename != null) {
if (filename.startsWith("=?") && filename.endsWith("?=")) {
filename = StandardMultipartHttpServletRequest.MimeDelegate.decode(filename);
}
files.add(part.getName(), new StandardMultipartHttpServletRequest.StandardMultipartFile(part, filename));
} else {
this.multipartParameterNames.add(part.getName());
}
}
this.setMultipartFiles(files);
} catch (Throwable var9) {
this.handleParseFailure(var9);
}
FileCopyUtils,实现文件流的拷贝



