简单了解springboot 中的multipart/formdata

2023年 9月 28日 53.7k 0

以下内容均由java17和springboot3.0演示产生。

从curl的上传方式说起

在curl 命令中,有两种上传文件的方式分别是:

  • curl --location 'http://10.211.55.2:9888/solution' --form 'file=@"/Users/bo/Downloads/alacritty.yml"' --form 'file2=@"/Users/bo/Downloads/goland-2023.2-aarch64.dmg"'
  • curl -T file https://example.com
  • 因此会给很多人造成困惑,这两种上传方式有什么区别?以及对应的服务端应该如何接收呢?

    方式一对应的http数据包

    curl --location 'http://10.211.55.2:9888/solution' \
    --form 'file=@"/Users/bo/Downloads/alacritty.yml"' \
    --form 'file2=@"/Users/bo/Downloads/goland-2023.2-aarch64.dmg"'
    
    POST /solution HTTP/1.1
    Host: 10.211.55.2:9888
    Content-Length: 373
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
    
    ------WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name="file"; filename="alacritty.yml"
    Content-Type: 
    
    (data)
    ------WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name="file2"; filename="goland-2023.2-aarch64.dmg"
    Content-Type: 
    
    (data)
    ------WebKitFormBoundary7MA4YWxkTrZu0gW--
    

    首先看第一种上传方式以及对应的http数据包,其中需要注意的是1、4、6、11、16行。其中第四行指定了该http的内容类型为multipart/form-data,每个文件内容的分隔符为----WebKitFormBoundary7MA4YWxkTrZu0gW,第六行则是真正的分隔符,需要在head中指定的分隔符前面添加两个--,第16行为最后一个分隔符,标志着http请求数据包的结束。

    以上的这种方式被称为multipart上传,其详细的细节可参考 rfc1867 。由于该上传方式是一种标准的上传方式,因此很多前端库都对其提供了较好的支持,大部分由前端向后端进行的文件上传操作,均是使用这种方式进行上传的。

    服务端要如何接收

    这里以springboot为例说,简要说明具体的处理逻辑。

  • 首先解释三个相关配置的参数
  • spring:
      servlet:
        multipart:
          # 这个参数决定multipart请求的总大小
          max-request-size: 5000MB
          # 这个参数决定multipart 单个文件的大小(待验证)
          max-file-size: 5000MB
          # 这个参数决定了是否由springboot帮你处理mutipart请求
          # 若是false的话那么你将得到原始的http流
          # 若是true的话,你将得到一个空的http流
          # 默认是true
    #      enabled: false
    
  • 代码中进行处理
  • public class SolutionsController {
    
        @PostMapping("/solution")
        public void uploadSolution(HttpServletRequest servletRequest) throws Exception {
            Enumeration headerNames = servletRequest.getHeaderNames();
            if (servletRequest instanceof StandardMultipartHttpServletRequest s) {
                //在这里处理所有的文件
                s.getFileMap();
            }
        }
    
    }
    

    springboot对于multipart请求会构造出一个StandardMultipartHttpServletRequest类型的request对象,我们只需要调用该对象的getFileMap()方法将就能拿到所有的文件名和文件流。

    这里需要额外注意,springboot会自动判断文件的大小,若文件较小,则直接将文件存在内存中;否则会将文件写入到一个临时的目录中,待请求结束时候自动删除临时文件

    方式二对应的http数据包

    curl -T Downloads/alacritty.yml -X POST http://localhost:9888/solution -vv
    
    POST /solution HTTP/1.1
    Host: localhost:9888
    User-Agent: curl/8.1.2
    Accept: */*
    Content-Length: 2355
    
    ""
    

    这种情况下,我们发现请求变得非常简短,甚至连Content-Type都没有了,并且在这种情况下,还需要额外的参数来指定将上传的文件的保存文件名。

    服务端如何接受

    这种情况后段要做的就非常简单了,直接获取http对象即可。

    public class SolutionsController {
    
        @PostMapping("/solution")
        public void uploadSolution(HttpServletRequest servletRequest) throws Exception {
            Enumeration headerNames = servletRequest.getHeaderNames();
            if (servletRequest instanceof StandardMultipartHttpServletRequest s) {
                //在这里处理所有的文件
                s.getFileMap();
            } else {
                // 这里处理普通文件流
                servletRequest.getInputStream();
            }
        }
    
    }
    

    总结

    在curl中存在两种上传文件的方式,一种为multipart方式上传,该方式可上传多个文件,同时是一种标准的上传方式,主要用作前端向后段传输数据,因此应用较为广泛。方式二则类似于一种原始二进制流的方式,需要额外的参数来指定文件名类型等信息,这种方式依赖于双方一致的约定,否则可能无法工作。知道上述两种方式的原理之后,就很容易在一个接口上实现对两种上传方式的兼容。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论