SpringBoot - 文件上传原理

来个例子

客户端

     <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);
    ......省略很多.....

	}

Guess you like

Origin blog.csdn.net/qq_27062249/article/details/118305016