在Spring Boot中上传单个或多个文件示例

x33g5p2x  于2022-09-18 转载在 Spring  
字(5.4k)|赞(0)|评价(0)|浏览(440)

在这篇文章中,我们将看到如何使用Spring Boot实现单个或多个文件的上传。

什么是multipart File上传请求?

在POST请求中,有三种类型。

  1. 请求正文是一堆URL-encoded的参数
  2. 请求正文是 RAW/二进制内容。(例如JSON)
  3. 请求正文是参数和RAW内容的混合体。(多部分表格数据)

在这篇文章中,我将更多地集中在第三种类型上。如果一个帖子请求的主体内容使用特定的边界或分隔符分割,那么它将被称为多部分请求。这个定界符将标志着单个参数的开始和结束。

例如,看一下下面这个表单。

如果这个表单被提交,那么服务器就会收到下面这种形式的HTTP请求。

POST /v3/ed8d9ae2-7d0d-4411-8b8c-66106d8a2721 HTTP/1.1
Host: run.mocky.io
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: 55480fab-4fc3-26e2-fde0-19cc88f8d73e

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="hello"

world
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="hotel"

trivago
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Code language: HTTP (http)

现在试着把请求中的确切数值和上面的截图配合起来。这里首先要注意的是Content-Type头。它将请求标记为多部分,每个参数之间的边界是----WebKitFormBoundary7MA4YWxkTrZu0gW。当服务器收到这个信息时,它可以很容易地分割请求的主体,并得到每个参数的名称和它们的适当值。

通过这种设置,如果一个参数本身是一个文件,我们可以简单地在边界内进行编码。如果我在上面的请求中加入一个文件参数,那么请求正文就会是这样的。

POST /v3/ed8d9ae2-7d0d-4411-8b8c-66106d8a2721 HTTP/1.1
Host: run.mocky.io
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: e139ea1a-1f88-ae45-72c6-867ab4a12209

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="hello"

world
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="hotel"

trivago
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="myfile"; filename="hello-world.txt"
Content-Type: text/plain

Hello world from a file

------WebKitFormBoundary7MA4YWxkTrZu0gW--
Code language: HTTP (http)

请注意,在请求中出现了一个新的部分,同时在正文中加入了文件的内容。有了这些知识,我们来写一些代码。

上传单个文件

假设我们的请求中有一个参数,我们想在我们的控制器方法中使用。例如,一个请求包含一个名为count的参数,它是一个整数。所以控制器方法的参数将是@RequestParam("count") Integer count。在这个例子中,Integer是存储参数计数的合适类型。

同样地,Spring MVC提供了一个MultipartFile来保存文件的内容。因此,这里是我的简单例子,以一个文件作为参数。

@RequestMapping(value = "/file-upload", method = RequestMethod.POST)
    @ResponseBody
    public String uploadFile(@RequestParam("myFile") MultipartFile multipartFile) throws IOException {
        multipartFile.transferTo(new File("C:\\data\\test\\" + multipartFile.getOriginalFilename()));
        return "success";
    }
Code language: Java (java)

在这个例子中,参数名称本身是myFile。另外,我把文件内容写到我的本地磁盘。至于你想对MultipartFile对象做什么,这取决于你。如果你有兴趣,这些是MultipartFile的可用方法部分。


helpful 方法来自MultipartFile类

上传两个文件

这很容易。只需添加另一个参数,用不同的@RequestParam

@RequestMapping(value = "/two-file-upload", method = RequestMethod.POST)
    @ResponseBody
    public String uploadTwoFile(@RequestParam("myFile") MultipartFile multipartFile, 
                                @RequestParam("myOtherFile") MultipartFile otherMultipartFile) throws IOException {
        multipartFile.transferTo(new File("C:\\data\\test\\" + multipartFile.getOriginalFilename()));
        otherMultipartFile.transferTo(new File("C:\\data\\test\\" + otherMultipartFile.getOriginalFilename()));
        return "success";
    }
Code language: Java (java)

在Spring Boot中上传多个文件

Spring MVC将不同值的同一个参数处理成一个数组或集合。这对MultipartFile参数也是如此。如果你必须上传相当多的文件,而不对每个文件参数名进行硬编码,这就是你要做的。

@RequestMapping(value = "/multiple-file-upload", method = RequestMethod.POST)
    @ResponseBody
    public String uploadMultipleFiles(@RequestParam("myFiles") MultipartFile[] multipartFiles) throws IOException {
        for (MultipartFile multipartFile : multipartFiles) {
            multipartFile.transferTo(new File("C:\\data\\test\\" + multipartFile.getOriginalFilename()));
        }
        return "success";
    }
Code language: Java (java)

限制MultipartFile的大小

是的,你可以限制上传到你系统中的最大文件大小。对于这一点,我们甚至不需要写任何一段代码。下面的属性将让客户上传大小低于5KB的文件。

spring.servlet.multipart.max-file-size=5KB
Code language: Properties (properties)

任何超过5KB的文件的请求将得到一个500内部服务器错误,并有一个org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException,如下所示。

2020-11-14 00:31:14.227 ERROR 16596 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field myFiles exceeds its maximum permitted size of 5120 bytes.] with root cause

org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field myFiles exceeds its maximum permitted size of 5120 bytes.
	at org.apache.tomcat.util.http.fileupload.impl.FileItemStreamImpl$1.raiseError(FileItemStreamImpl.java:114) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
	at org.apache.tomcat.util.http.fileupload.util.LimitedInputStream.checkLimit(LimitedInputStream.java:76) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
	at org.apache.tomcat.util.http.fileupload.util.LimitedInputStream.read(LimitedInputStream.java:135) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
	at java.io.FilterInputStream.read(FilterInputStream.java:107) ~[na:1.8.0_221]
Code language: Java (java)

请注意,这个配置的默认值是1MB

无限上传大小

尽管这通常是个坏主意,你可以通过设置max-file-size为-1,将上传大小设置为无限。

spring.servlet.multipart.max-file-size=-1
spring.servlet.multipart.max-request-size=-1
Code language: Properties (properties)

如果你还在使用spring boot 1.x版本,你应该对这些属性使用spring.http而不是spring.servlet

使用lazy解析法

Spring MVC会立即解析所有的MultipartFile。如果被解析的文件可能会也可能不会被进一步使用,那么最好只在文件确实被使用时才推迟解析。这样我们可以节省CPU的使用。对于这种行为,你需要在应用程序属性中添加以下配置。

spring.servlet.multipart.resolve-lazily=true
Code language: Properties (properties)

上述所有例子的源代码都可以在下面的GitHub链接中找到。如果你想对这个话题进行补充或有任何问题,请随时在下面留言。

相关文章