9、SpringBoot2 Web开发之文件上传及其原理

文章详细介绍了SpringBoot中实现文件上传的过程,包括前端HTML表单设置,后端Controller接收MultipartFile,以及SpringBoot自动配置的文件解析器工作原理,如StandardServletMultipartResolver和MultipartProperties的配置。
摘要由CSDN通过智能技术生成

1、文件上传

1.1 前端页面

使用表单进行提交,提交的类型为multipart/form-data

<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
	<div class="form-group">
		<label for="exampleInputFile">头像</label>
		<input type="file" name="headerImg" id="exampleInputFile">
	</div>
	<div class="form-group">
		<label for="exampleInputFile">生活照</label>
		<!-- multiple支持多文件上传 -->
		<input type="file" name="photos" multiple>
	</div>
	<button type="submit" class="btn btn-primary">提交</button>
</form>

1.2 后端controller接收

RequestPart标注接收文件提交的名称,MultipartFile接收文件

    @PostMapping("/upload")
   public String upload(@RequestPart("headerImg") MultipartFile headerImg,
                        @RequestPart("photos") MultipartFile[] photos) throws IOException {
       System.out.println(headerImg.getSize());
       System.out.println(photos.length);
       headerImg.transferTo(new File("E://aaaa.png"));//将文件输出到指定位置
       return null;
}

2 文件上传原理

2.1 文件解析器自动配置

springBoot启动时,会将容器中自动注入StandardServletMultipartResolver文件解析器

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class})
@ConditionalOnProperty(
    prefix = "spring.servlet.multipart",
    name = {"enabled"},
    matchIfMissing = true
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({MultipartProperties.class})
public class MultipartAutoConfiguration {
    private final MultipartProperties multipartProperties;

    public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
        this.multipartProperties = multipartProperties;
    }

    @Bean
    @ConditionalOnMissingBean({MultipartConfigElement.class, CommonsMultipartResolver.class})
    public MultipartConfigElement multipartConfigElement() {
        return this.multipartProperties.createMultipartConfig();
    }

    @Bean(
        name = {"multipartResolver"}
    )
    @ConditionalOnMissingBean({MultipartResolver.class})
    public StandardServletMultipartResolver multipartResolver() {
        StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
        multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
        return multipartResolver;
    }
}

可通过修改MultipartProperties配置,对文件进行配置

@ConfigurationProperties(
    prefix = "spring.servlet.multipart",
    ignoreUnknownFields = false
)
public class MultipartProperties {
    private boolean enabled = true;
    private String location;
    private DataSize maxFileSize = DataSize.ofMegabytes(1L);// 单个数据的大
    private DataSize maxRequestSize = DataSize.ofMegabytes(10L);//总数据的大小
    private DataSize fileSizeThreshold = DataSize.ofBytes(0L);
    private boolean resolveLazily = false;
}

2.2 文件上传自动解析流程

2.2.1 将request封装成MultipartHttpServletRequest

请求进来使用文件上传解析器判断(isMultipart),并使用resolveMultipart方法将请求封装成MultipartHttpServletRequest并返回。

//DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//..............................
	processedRequest = checkMultipart(request);
	//............................
}

//DispatcherServlet
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
	//....................
	if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
		//....................
		return this.multipartResolver.resolveMultipart(request);
		//....................
	}
}

//StandardServletMultipartResolver
//所以请求类型必须为multipart
public boolean isMultipart(HttpServletRequest request) {
    return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}

//StandardServletMultipartResolver
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
    return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}

2.2.2 使用参数解析器解析参数

文件上传参数MultipartFile的参数解析器为RequestPartMethodArgumentResolver

2.2.2.1 判断是否支持

标注了RequestPart注解,或者是MultipartFile类型或者是Part类型

//RequestPartMethodArgumentResolver
public boolean supportsParameter(MethodParameter parameter) {
	if (parameter.hasParameterAnnotation(RequestPart.class)) {
		return true;
	}
	else {
		if (parameter.hasParameterAnnotation(RequestParam.class)) {
			return false;
		}
		return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional());
	}
}
//MultipartResolutionDelegate
public static boolean isMultipartArgument(MethodParameter parameter) {
     Class<?> paramType = parameter.getNestedParameterType();
     return MultipartFile.class == paramType || isMultipartFileCollection(parameter) || isMultipartFileArray(parameter) || Part.class == paramType || isPartCollection(parameter) || isPartArray(parameter);
 }
2.2.2.2 对参数进行解析逻辑
//MultipartResolutionDelegate
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	//...................
	Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest)
	//..................
}

//MultipartResolutionDelegate
public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request) throws Exception {
	//获取MultipartHttpServletRequest,并将文集流封装成MultipartFile
	//调用本地方法
	MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
	//.........................
}

//根据名称生成的数组multipartFiles中获取对应的MultipartFile
public List<MultipartFile> getFiles(String name) {
     List<MultipartFile> multipartFiles = (List)this.getMultipartFiles().get(name);
     return multipartFiles != null ? multipartFiles : Collections.emptyList();
 }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值