自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從零搭建開(kāi)發(fā)腳手架 Spring Boot文件上傳的多種方式、原理及遇到的問(wèn)題

開(kāi)發(fā) 架構(gòu)
Spring支持可插拔的MultipartResolver對(duì)象進(jìn)行文件上傳。目前有2個(gè)實(shí)現(xiàn);在Servlet 2.5 及早期版本之前,文件上傳需要借助 commons-fileupload 組件來(lái)實(shí)現(xiàn)。從Servlet 3.0規(guī)范之后,提供了對(duì)文件上傳的原生支持,進(jìn)一步簡(jiǎn)化了應(yīng)用程序的實(shí)現(xiàn)。

[[386968]]

 本文轉(zhuǎn)載自微信公眾號(hào)「Java大廠面試官」,作者laker。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java大廠面試官公眾號(hào)。  

文件上傳

概述

Spring支持可插拔的MultipartResolver對(duì)象進(jìn)行文件上傳。目前有2個(gè)實(shí)現(xiàn);

  • 在Servlet 2.5 及早期版本之前,文件上傳需要借助 commons-fileupload 組件來(lái)實(shí)現(xiàn)。
  • 從Servlet 3.0規(guī)范之后,提供了對(duì)文件上傳的原生支持,進(jìn)一步簡(jiǎn)化了應(yīng)用程序的實(shí)現(xiàn)。

commons-fileupload

要使用commons-fileupload的CommonsMultipartResolver處理文件上傳,我們需要添加以下依賴(lài)項(xiàng):

  1. <dependency> 
  2.     <groupId>commons-fileupload</groupId> 
  3.     <artifactId>commons-fileupload</artifactId> 
  4. </dependency> 

配置定義CommonsMultipartResolver bean。

  1. @Bean(name = "multipartResolver"
  2. public CommonsMultipartResolver multipartResolver() { 
  3.     CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); 
  4.     multipartResolver.setMaxUploadSize(100000); 
  5.     return multipartResolver; 

Servlet 3.0

SpringBoot項(xiàng)目參見(jiàn)MultipartAutoConfiguration.java類(lèi),默認(rèn)會(huì)自動(dòng)配置StandardServletMultipartResolver,我們不需要做任何事情,就能使用了。

  1. @Configuration(proxyBeanMethods = false
  2. @ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class }) 
  3. @ConditionalOnProperty(prefix = "spring.servlet.multipart"name = "enabled", matchIfMissing = true
  4. @ConditionalOnWebApplication(type = Type.SERVLET) 
  5. @EnableConfigurationProperties(MultipartProperties.class) 
  6. public class MultipartAutoConfiguration { 
  7.  
  8.  private final MultipartProperties multipartProperties; 
  9.  
  10.  public MultipartAutoConfiguration(MultipartProperties multipartProperties) { 
  11.   this.multipartProperties = multipartProperties; 
  12.  } 
  13.  
  14.  @Bean 
  15.  @ConditionalOnMissingBean({ MultipartConfigElement.class, CommonsMultipartResolver.class }) 
  16.  public MultipartConfigElement multipartConfigElement() { 
  17.   return this.multipartProperties.createMultipartConfig(); 
  18.  } 
  19.  
  20.  @Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) 
  21.  @ConditionalOnMissingBean(MultipartResolver.class) 
  22.  public StandardServletMultipartResolver multipartResolver() { 
  23.   StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver(); 
  24.   multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily()); 
  25.   return multipartResolver; 
  26.  } 
  27.  

常見(jiàn)文件上傳相關(guān)需求,我整理總結(jié)如下:

單文件上傳

前端核心代碼

  1. <form method="POST" action="/upload-file" enctype="multipart/form-data"
  2.     <table
  3.         <tr> 
  4.             <td><input type="file" name="file" /></td> 
  5.         </tr> 
  6.         <tr> 
  7.             <td><input type="submit" value="Submit" /></td> 
  8.         </tr> 
  9.     </table
  10. </form> 

 

 

 

后端核心代碼

  1. @RequestMapping(value = "/upload-file", method = RequestMethod.POST) 
  2. public String submit(@RequestParam("file") MultipartFile file) { 
  3.     return "ok"

 

 

 

多文件上傳

前端核心代碼

 

  1. <form method="POST" action="/upload-files" enctype="multipart/form-data"
  2.     <table
  3.         <tr> 
  4.             <td>Select a file to upload</td> 
  5.             <td><input type="file" name="files" /></td> 
  6.         </tr> 
  7.         <tr> 
  8.             <td>Select a file to upload</td> 
  9.             <td><input type="file" name="files" /></td> 
  10.         </tr> 
  11.         <tr> 
  12.             <td>Select a file to upload</td> 
  13.             <td><input type="file" name="files" /></td> 
  14.         </tr> 
  15.         <tr> 
  16.             <td><input type="submit" value="Submit" /></td> 
  17.         </tr> 
  18.     </table
  19. </form> 

 

后端核心代碼

我們需要注意每個(gè)輸入字段具有相同的名稱(chēng),以便可以將其作為MultipartFile數(shù)組進(jìn)行訪(fǎng)問(wèn):

  1. @RequestMapping(value = "/upload-files", method = RequestMethod.POST) 
  2.    public String submit(@RequestParam("files") MultipartFile[] files) { 
  3.        return "ok"
  4.    } 

帶其他參數(shù)的文件上傳

前端核心代碼

  1. <form method="POST" action="/upload-files-with-data" enctype="multipart/form-data"
  2.     <table
  3.         <tr> 
  4.             <td>Name</td> 
  5.             <td><input type="text" name="name" /></td> 
  6.         </tr> 
  7.         <tr> 
  8.             <td>Email</td> 
  9.             <td><input type="text" name="email" /></td> 
  10.         </tr> 
  11.         <tr> 
  12.             <td>Select a file to upload</td> 
  13.             <td><input type="file" name="file" /></td> 
  14.         </tr> 
  15.         <tr> 
  16.             <td><input type="submit" value="Submit" /></td> 
  17.         </tr> 
  18.     </table
  19. </form> 

 

后端核心代碼

“在控制器中,我們可以使用@RequestParam注解獲取所有表單數(shù)據(jù),也可以不使用@RequestParam獲取

  1. @PostMapping("/upload-files-with-data"
  2. public String submit( 
  3.             @RequestParam MultipartFile file, @RequestParam String name
  4.             String email) { 
  5.     return "ok"

優(yōu)雅的后端實(shí)現(xiàn)

我們還可以將所有表單字段封裝在類(lèi)中,當(dāng)文件中有很多其他字段時(shí),就很方便。

  1. public class FormDataWithFile { 
  2.     private String name
  3.     private String email; 
  4.     private MultipartFile file; 
  5. @PostMapping("/upload-files-with-data"
  6. public String submit(FormDataWithFile formDataWithFile) { 
  7.     return "ok"

多個(gè)(文件+參數(shù))上傳

功能需求類(lèi)似于上傳如下請(qǐng)求:

  1.     { 
  2.         "name""a"
  3.         "emainl""b"
  4.         "file"
  5.     }, 
  6.     { 
  7.         "name""a"
  8.         "emainl"""
  9.         "file"
  10.     } 

但是這樣寫(xiě)是行不通的,解決方案如下:

方案一:上傳文件Base64

把文件轉(zhuǎn)為base64字符串,但是轉(zhuǎn)換后的字符串大小是原圖片大小的3倍。(慎用)

  1.     { 
  2.         "name""a"
  3.         "emainl"""
  4.         "fileBase64":"xxxxx" 
  5.     }, 
  6.     { 
  7.         "name""b"
  8.         "emainl"""
  9.         "fileBase64":"xxxxx" 
  10.     } 

方案二:上傳文件url

先把圖片上傳到服務(wù)器,獲取文件url,然后再把文件的URL與其他參數(shù)上傳到后端

  1.     { 
  2.         "name""a"
  3.         "emainl"""
  4.         "fileUrl":"xxxxx.png" 
  5.     }, 
  6.     { 
  7.         "name""b"
  8.         "emainl"""
  9.         "fileUrl":"xxxxx.png" 
  10.     } 

文件上傳原理

通常一個(gè)文件上傳的請(qǐng)求內(nèi)容格式如下:

  1. POST /upload HTTP/1.1  
  2. Host:xxx.org  
  3. Content-type: multipart/form-data, boundary="boundaryStr" 
  4.  
  5. --boundaryStr 
  6. content-disposition: form-data; name="name" 
  7.  
  8. Name Of Picture 
  9. --boundaryStr 
  10. Content-disposition: attachment; name="picfile"; filename="picfile.gif" 
  11. Content-type: image/gif 
  12. Content-Transfer-Encoding: binary 
  13.  
  14. ...contents of picfile.gif... 

其中 boundary 指定了內(nèi)容分割的邊界字符串;

Content-dispostion 指定了這是一個(gè)附件(文件),包括參數(shù)名稱(chēng)、文件名稱(chēng);

Content-type 指定了文件類(lèi)型;

Content-Transfer-Encoding 指定內(nèi)容傳輸編碼;

Tomcat 實(shí)現(xiàn)了 Servlet3.0 規(guī)范,通過(guò)ApplicationPart對(duì)文件上傳流實(shí)現(xiàn)封裝, 其中,DiskFileItem 描述了上傳文件實(shí)體,在請(qǐng)求解析時(shí)生成該對(duì)象, 需要關(guān)注的是,DiskFileItem 聲明了一個(gè)臨時(shí)文件,用于臨時(shí)存儲(chǔ)上傳文件的內(nèi)容, SpringMVC 對(duì)上層的請(qǐng)求實(shí)體再次封裝,最終構(gòu)造為MultipartFile傳遞給應(yīng)用程序。示例如下:

 

生成的臨時(shí)文件如下:

這個(gè)是臨時(shí)文件的目錄,可以配置的

 

圖片

臨時(shí)文件打開(kāi),查看其內(nèi)容如下:

 

參數(shù):name

 

參數(shù):file

上傳完畢后,臨時(shí)文件會(huì)刪除

 

“可以看到,不是file類(lèi)型的參數(shù)也會(huì)寫(xiě)入到臨時(shí)文件。

通過(guò)Fiddler進(jìn)行抓包:

  1. POST http://localhost:8080/upload-files-with-data HTTP/1.1 
  2. cache-control: no-cache 
  3. Accept: */* 
  4. Host: localhost:8080 
  5. accept-encoding: gzip, deflate 
  6. content-type: multipart/form-data; boundary=--------------------------895818005136536360125479 
  7. content-length: 268707 
  8. Connection: keep-alive 
  9.  
  10. ----------------------------895818005136536360125479 
  11. Content-Disposition: form-data; name="name" 
  12.  
  13. 123 
  14. ----------------------------895818005136536360125479 
  15. Content-Disposition: form-data; name="file"; filename="test.txt" 
  16. Content-Type: text/plain 
  17.  
  18. abc123 
  19. ----------------------------895818005136536360125479 
  20. Content-Disposition: form-data; name="file"; filename="1114289-20190110120111312-1475461850.png" 
  21. Content-Type: image/png 
  22.  
  23. ...contents of png... 
  24. ----------------------------895818005136536360125479-- 

到這里,我們就大概就知道了HTTP上傳文件的原理了。HTTP把需要上傳的表單的所有數(shù)據(jù)按照一定的格式存放在請(qǐng)求體中,對(duì)于文件也是同樣的。

  • Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqj67FUBQUHXZj78G表示要上傳附件,
  • 其中boundary表示分隔符,如果表單中有多項(xiàng),就要使用boundary進(jìn)行分隔,每個(gè)表單項(xiàng)由------FormBoundary開(kāi)始,以------FormBoundary結(jié)尾。例如這樣:
  1. ------FormBoundary 
  2. Content-Disposition: form-data; name="param1" 
  3.  
  4. value1 
  5. ------FormBoundary 

這個(gè)boundary的值是由瀏覽器生成的,由瀏覽器來(lái)保證與上傳內(nèi)容不重復(fù)。

  • 在每個(gè)分隔項(xiàng)里,需要我們?nèi)ブ攸c(diǎn)關(guān)注Content-Disposition消息頭,其中第一個(gè)參數(shù)總是固定不變的form-data,name表示表單元素屬性名,回車(chē)換行符后面的內(nèi)容就是元素的值。還有Content-Type表示我們上傳的文件的MIME類(lèi)型,我們?cè)诜?wù)器端需要根據(jù)這個(gè)進(jìn)行文件的區(qū)分。
  • 最后一個(gè)boundary的結(jié)尾會(huì)多兩個(gè)--

HTTP就是按照這種格式,把表單中的數(shù)據(jù)封裝成一個(gè)請(qǐng)求一股腦的發(fā)給服務(wù)器端,服務(wù)器端根據(jù)這種規(guī)則對(duì)接收到的請(qǐng)求進(jìn)行解析,從而完成文件上傳功能。

下面是從網(wǎng)上找的一個(gè)后臺(tái)解析示例??梢訢EBUG跟蹤代碼去分析。

  1. @WebServlet(urlPatterns = "/lakerfile"
  2. public class FileUploadDemo extends HttpServlet { 
  3.     @Override 
  4.     public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  5.         DiskFileItemFactory fac = new DiskFileItemFactory(); 
  6.         ServletFileUpload upload = new ServletFileUpload(fac); 
  7.         upload.setFileSizeMax(10 * 1024 * 1024); 
  8.         upload.setSizeMax(20 * 1024 * 1024); 
  9.         if (ServletFileUpload.isMultipartContent(request)) { // 只處理Multipart請(qǐng)求 
  10.                 List<FileItem> list = upload.parseRequest(new ServletRequestContext(request));// 解析報(bào)文 
  11.                 for (FileItem item : list) { 
  12.                     if (item.isFormField()) { 
  13.                         String fileName = item.getFieldName(); 
  14.                         String value = item.getString("UTF-8"); 
  15.                     } else { 
  16.                         File file = new File(realPath, name); 
  17.                         item.write(file); 
  18.                         ... 
  19.         } 
  20.     } 

遇到的問(wèn)題

Spring Boot上傳文件大小限制

  1. spring: 
  2.   servlet: 
  3.     multipart: 
  4.       # 最大文件大小(單個(gè)) 
  5.       max-file-size: 10MB 
  6.       # 文件大于該閾值時(shí),將寫(xiě)入磁盤(pán),支持B/KB/MB單位 
  7.       file-size-threshold: 0B 
  8.       # //最大請(qǐng)求大小(總體) 
  9.       max-request-size: 100MB 

這幾個(gè)參數(shù)由SpringMVC控制,用于注入 Servlet3.0 的文件上傳配置,關(guān)聯(lián)類(lèi)如下:

  1. public class MultipartConfigElement { 
  2.     private final String location;// = ""
  3.     private final long maxFileSize;// = -1; 
  4.     private final long maxRequestSize;// = -1; 
  5.     private final int fileSizeThreshold;// = 0; 

上傳文件過(guò)大異常攔截

  1. @ExceptionHandler(MaxUploadSizeExceededException.class) 
  2. public Response handleMaxSizeException(MaxUploadSizeExceededException e) { 
  3.     log.error(e.getMessage(), e); 
  4.     return Response.error(500, "File too large!"); 

自定義tomcat工作目錄

自定義臨時(shí)文件生成目錄

  1. server: 
  2.   tomcat: 
  3.     basedir: /laker/tmp 

使用swagger上傳文件不起作用

  • allowMultiple=true:表示是數(shù)組格式的參數(shù)
  • dataType = "__file":表示數(shù)組中參數(shù)的類(lèi)型
  1. @ApiOperation(value = "上傳", notes = "上傳"
  2. @ApiImplicitParams({ 
  3.             @ApiImplicitParam(paramType = "form"name = "file", value = "文件對(duì)象", required = true, dataType = "__file"), 
  4.             @ApiImplicitParam(paramType = "form"name = "files", value = "文件數(shù)組", allowMultiple = true, dataType = "__file"
  5.     }) 
  6. public void test(@RequestParam("file") MultipartFile file, @RequestParam(value = "files", required = false) MultipartFile[] files) throws Exception { 

參考:

https://www.cnblogs.com/yougewe/p/12916211.html

 

https://www.baeldung.com/spring-file-upload

 

責(zé)任編輯:武曉燕 來(lái)源: Java大廠面試官
相關(guān)推薦

2020-08-19 08:55:47

Redis緩存數(shù)據(jù)庫(kù)

2021-07-13 18:42:38

Spring Boot腳手架開(kāi)發(fā)

2021-09-01 10:07:43

開(kāi)發(fā)零搭建Groovy

2021-03-09 17:11:09

數(shù)據(jù)庫(kù)腳手架開(kāi)發(fā)

2021-04-28 16:10:48

開(kāi)發(fā)腳手架 Spring

2021-07-29 18:49:49

Spring開(kāi)發(fā)腳手架

2021-02-19 22:43:50

開(kāi)發(fā)腳手架Controller

2021-05-13 17:02:38

MDC腳手架日志

2021-04-13 14:47:53

認(rèn)證授權(quán)Java

2021-04-20 19:24:16

腳手架 Java微信

2021-06-02 17:58:49

腳手架 冪等性前端

2020-06-29 11:35:02

Spring BootJava腳手架

2016-08-10 14:59:41

前端Javascript工具

2021-01-07 05:34:07

腳手架JDK緩存

2023-11-21 17:36:04

OpenFeignSentinel

2021-05-21 05:22:52

腳手架工具項(xiàng)目

2018-08-30 16:08:37

Node.js腳手架工具

2018-06-11 14:39:57

前端腳手架工具node.js

2024-01-23 08:47:13

BeanSpring加載方式

2022-07-18 07:58:46

Spring工具工具類(lèi)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)