日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
從零搭建開發(fā)腳手架SpringBoot文件上傳的多種方式、原理及遇到的問題

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

文件上傳

概述

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

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

commons-fileupload

要使用commons-fileupload的CommonsMultipartResolver處理文件上傳,我們需要添加以下依賴項:

 
 
 
 
  1.     commons-fileupload
  2.     commons-fileupload

配置定義CommonsMultipartResolver bean。

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

Servlet 3.0

SpringBoot項目參見MultipartAutoConfiguration.java類,默認(rèn)會自動配置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.  private final MultipartProperties multipartProperties;
  8.  public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
  9.   this.multipartProperties = multipartProperties;
  10.  }
  11.  @Bean
  12.  @ConditionalOnMissingBean({ MultipartConfigElement.class, CommonsMultipartResolver.class })
  13.  public MultipartConfigElement multipartConfigElement() {
  14.   return this.multipartProperties.createMultipartConfig();
  15.  }
  16.  @Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
  17.  @ConditionalOnMissingBean(MultipartResolver.class)
  18.  public StandardServletMultipartResolver multipartResolver() {
  19.   StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
  20.   multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
  21.   return multipartResolver;
  22.  }
  23. }

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

單文件上傳

前端核心代碼

 
 
 
 
  1.     
  2.         
  3.             
  4.         
  5.         
  6.             
  7.         
  8.     

后端核心代碼

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

多文件上傳

前端核心代碼

  
 
 
 
  1.     
  2.         
  3.             
  4.             
  5.         
  6.         
  7.             
  8.             
  9.         
  10.         
  11.             
  12.             
  13.         
  14.         
  15.             
  16.         
  17.     
  18. Select a file to upload
    Select a file to upload
    Select a file to upload

后端核心代碼

我們需要注意每個輸入字段具有相同的名稱,以便可以將其作為MultipartFile數(shù)組進行訪問:

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

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

前端核心代碼

 
 
 
 
  1.     
  2.         
  3.             
  4.             
  5.         
  6.         
  7.             
  8.             
  9.         
  10.         
  11.             
  12.             
  13.         
  14.         
  15.             
  16.         
  17.     
  18. Name
    Email
    Select a file to upload

后端核心代碼

“在控制器中,我們可以使用@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";
  6. }

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

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

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

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

功能需求類似于上傳如下請求:

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

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

方案一:上傳文件Base64

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

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

方案二:上傳文件url

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

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

文件上傳原理

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

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

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

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

Content-type 指定了文件類型;

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

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

生成的臨時文件如下:

這個是臨時文件的目錄,可以配置的

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

參數(shù):name

參數(shù):file

上傳完畢后,臨時文件會刪除

“可以看到,不是file類型的參數(shù)也會寫入到臨時文件。

通過Fiddler進行抓包:

 
 
 
 
  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. ----------------------------895818005136536360125479
  10. Content-Disposition: form-data; name="name"
  11. 123
  12. ----------------------------895818005136536360125479
  13. Content-Disposition: form-data; name="file"; filename="test.txt"
  14. Content-Type: text/plain
  15. abc123
  16. ----------------------------895818005136536360125479
  17. Content-Disposition: form-data; name="file"; filename="1114289-20190110120111312-1475461850.png"
  18. Content-Type: image/png
  19. ...contents of png...
  20. ----------------------------895818005136536360125479--

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

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

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

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

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

下面是從網(wǎng)上找的一個后臺解析示例??梢訢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請求
  10.                 List list = upload.parseRequest(new ServletRequestContext(request));// 解析報文
  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.     }
  21. }

遇到的問題

Spring Boot上傳文件大小限制

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

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

 
 
 
 
  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;

上傳文件過大異常攔截

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

自定義tomcat工作目錄

自定義臨時文件生成目錄

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

使用swagger上傳文件不起作用

  • allowMultiple=true:表示是數(shù)組格式的參數(shù)
  • dataType = "__file":表示數(shù)組中參數(shù)的類型
 
 
 
 
  1. @ApiOperation(value = "上傳", notes = "上傳")
  2. @ApiImplicitParams({
  3.             @ApiImplicitParam(paramType = "form", name = "file", value = "文件對象", 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 {
  7. }

參考:

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

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


網(wǎng)站欄目:從零搭建開發(fā)腳手架SpringBoot文件上傳的多種方式、原理及遇到的問題
轉(zhuǎn)載源于:http://m.5511xx.com/article/djhehoi.html