当前位置: 首页 > 后端技术 > Java

SpringBoot文件上传的使用及原理

时间:2023-04-02 01:03:18 Java

1.使用基本配置步骤:构建文件上传表单,表单必须post提交,编码方式为multipart/form-dataController方法参数类型为MultipartFile,注解是@RequestPart1,构建文件上传表单邮箱

名称
头像示例块级帮助文本在这里。

生活照片
提交2,文件上传代码//表单提交必须使用post请求@PostMapping("/upload")publicStringupload(@RequestParam("email")Stringemail,@RequestParam("username")Stringusername,@RequestPart("headerImg")MultipartFileheaderImg,//MultipartFile:用于上传文件@RequestPart:以@RequestPart("photos")MultipartFile[]photos)throwsIOException{log.info("上传信息:email={},username={},headerImg={},photos={}",email,username,headerImg.getSize(),photos.length);if(!headerImg.isEmpty()){//isEmpty,getOriginalFilename,transferTo:都是MultipartFile接口中的方法StringoriginalFilename=headerImg.getOriginalFilename();headerImg.transferTo(newFile("D:\\cache\\"+originalFilename));//transferTo:文件保存(传输)到哪里}if(photos.length>0){for(MultipartFilephoto:photos){if(!photo.isEmpty()){StringoriginalFilename=photo.getOriginalFilename();photo.transferTo(newFile("D:\\cache\\"+originalFilename));}}}return"main";}}如果上传超过大小限制异常org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException:字段profile-photo超出了其最大允许大小1048576字节。可以修改底层默认限制文件大小:spring.servlet.multipart.max-file-size=10#单个文件最大大小#多个文件上传,一次提交的总最大大小,默认10MB,改为100spring.servlet.multipart.max-request-size=1002.原理文件上传的自动配置类是MultipartAutoConfiguration,所有文件上传相关的配置都封装在MultipartProperties中,在springboot中,StandardServletMultipartResolver【文件上传解析器】已经被自动配置。只能解析标准的servlet方式,相当于servlet协议上传的文件。如果是自定义方式,直接上传的方式应该写自定义的文件上解析器@Configuration(proxyBeanMethods=false)@ConditionalOnClass({Servlet.class,StandardServletMultipartProtyResolver.class,MultipartConfigElement.class)@Conditionalprefix()"spring.servlet.multipart",name={"enabled"},matchIfMissing=true)@ConditionalOnWebApplication(type=Type.SERVLET)@EnableConfigurationProperties({MultipartProperties.class})publicclassMultipartAutoConfiguration{privatefinalMultipartPropertiesmultipartProperties;publicMultipartAutoConfiguration(MultipartPropertiesmultipartProperties){this.multipartProperties=multipartProperties;}@Bean@ConditionalOnMissingBean({MultipartConfigElement.class,CommonsMultipartResolver.class})publicMultipartConfigElementmultipartConfigElement(){returnthis.multipartProperties.createMultipartConfig();}@Bean(name={"multipartResolver"})@ConditionalOnMissingBean({MultipartResolver.class})publicStandardServletMultipartResolvermultipartResolver(){StandardServletMultipartResolvermultipartResolver=newStandardServletMultipartResolver();multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());使用isMultipart方法判断)并封装(用resolveMultipart方法封装,返回MultipartHttpServletRequest类型)文件上传请求1.请求被DispatcherServlet的doDispatch()拦截。在选择使用哪个解析器处理请求之前,会先调用checkMultipart()检查当前请求是否为文件上传请求2.checkMultipart()逻辑:判断容器中是否存在multipartResolver文件上传解析器并判断当前请求是否为文件上传请求。文件上传解析器是否在容器中取决于MultipartAutoConfiguration是否已经给我们将文件上传解析器放在容器中,可以看到MultipartAutoConfiguration类中的multipartResolver()方法使用了@ConditionalOnMissingBean注解,所以如果我们没有编写自定义文件上传解析器,SpringBoot会自动将StandardServletMultipartResolver文件上传注入到容器解析器中Determinewhetherthecurrentrequestisafileuploadrequest:publicbooleanisMultipart(HttpServletRequestrequest){//DeterminewhethertheContentTypeoftherequeststartswithmultipart/,becauseourformsettingenctype="multipart/form-data",sothisisafileuploadTherequestreturnStringUtils.startsWithIgnoreCase(request.getContentType(),"multipart/");}3.Ifitisafileuploadrequest,usethemultipartResolverfileuploadresolverinthecontainertoresolvetherequestreturnthis.multipartResolver.resolveMultipart(request);并把原生的request封装成StandardMultipartHttpServletRequest类,然后所有的文件上传请求最终会被返回一个叫MultipartHttpServletRequest对象publicMultipartHttpServletRequestresolveMultipart(HttpServletRequestrequest)throwsMultipartException{returnnewStandardMultipartHttpServletRequest(request,this.resolveLazily);}因为前面的checkMultipart()willreturnapackagedrequest,sothepackagedrequestisnotequaltotheoriginalrequest,thenitisafileuploadrequest,andtheattributevalueofmultipartRequestParsedbecomestrue2.JudgingthefileuploadrequestWhichparameterparserisusedtofinallydeterminetheparametervalue1.FindtheparameterparserandcometothehandlermethodofDispatcherServletandenterandthenenterthismethod:re-enterthismethod:re-enterthismethod:re-enterthismethod:re-enterThismethod:re-entry:determinewhetherthecurrentparsersupportsthelogicofparsingthisparameter:traverseallparameterparsers,entertheresolveArgumentmethod,andthenentergetArgumentResolver方法获取参数解析器:终于找到了这个参数解析器(如果参数前面标注了@RequestPart注解,就会使用这个参数解析器来解析参数)2.解析请求中的文件内容并封装intoaMultipartFile获取参数解析后,看他怎么解析(RequestPartMethodArgumentResolver)进入参数解析过程:进入resolveMultipartArgument方法:进入getFiles方法,然后进入getMultipartFiles方法。该方法将请求中的文件信息封装成一个Map返回;MultiValueMap因为参数的位置是这样写的,所以相当于把所有的headerImg和photos都封装到map里面了。如果想获取headerImg中的数据,可以从地图中取出来,photo也是@PostMapping("/upload")publicStringupload(@RequestParam("email")Stringemail,@RequestParam("username")Stringusername,@RequestPart("headerImg")MultipartFileheaderImg,@RequestPart("photos")MultipartFile[]photos)最后通过文件上传解析器上传所有文件,得到文件信息。总结:1、请求被DispatcherServlet的doDispatch()拦截。2、首先调用checkMultipart()检查当前请求是否为文件上传请求,容器中是否存在文件上传解析器。使用isMultipart方法判断是否为文件上传请求(请求的ContentType是否以multipart/开头)判断容器中是否存在multipartResolver文件上传解析器(如果没有写自定义文件上传解析器,文件上传解析器会为我们放在容器中)3.如果是文件上传请求,使用容器中的multipartResolver.resolveMultipart()文件上传解析器解析请求,最后将原始请求封装(封装)成一个MultipartHttpServletRequest类并返回4.遍历所有参数解析器,最后找到RequestPartMethodArgumentResolver参数解析器,调用该参数的解析参数方法解析文件内容并将文件信息封装成一个Map返回。MultipartFile接口中还有一个很强大的方法publicinterfaceMultipartFileextendsInputStreamSource{//获取形式为getName()的属性名String;//获取原始文件名@NullableStringgetOriginalFilename();//获取内容类型(multipart/form-data)@NullableStringgetContentType();//判断当前文件是否为空booleanisEmpty();//当前大小longgetSize();//获取字节流byte[]getBytes()throwsIOException;//获取输入流InputStreamgetInputStream()throwsIOException;//获取文件资源的路径信息defaultResourcegetResource(){returnnewMultipartFileResource(this);}//WheretosavethefilevoidtransferTo(Filevar1)throwsIOException,IllegalStateException;defaultvoidtransferTo(Pathdest)throwsIOException,IllegalStateException{//FileCopyUtils文件复制工具类FileCopyUtils.copy(this.getInputStream(),Files.newOutputStream(dest));}}----------------FileCopyUtils---------------------------//哪里复制文件:实现文件流的复制publicstaticintcopy(Filein,Fileout)throwsIOException{Assert.notNull(in,"NoinputFilespecified");Assert.notNull(out,"没有指定输出文件");返回副本(Files.newInputStream(in.toPath()),Files.newOutputStream(out.toPath()));}分析原理从两处入手,一是springboot这个功能是不是为了他做自动配置,什么样的自动配置,二是调试源码看看这个功能是怎么实现的