【问题标题】:Cannot get Spring Boot to lazily resolve a multipart file无法让 Spring Boot 懒惰地解析多部分文件
【发布时间】:2018-08-20 10:41:09
【问题描述】:

我使用 Spring Initializr 创建了一个 Spring Boot 2 演示应用程序,并在下面添加了控制器:

@Controller
@RequestMapping("/demo")
public class UploadController {
    private final static Logger LOG =   LoggerFactory.getLogger(UploadController.class);

    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(
        @RequestParam("metadata") MultipartFile metadata,
        @RequestParam("payload") MultipartFile payload) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Map metadataMap = mapper.readValue(metadata.getInputStream(), Map.class);
        LOG.info("Received call to upload file {}", metadataMap.get("filename"));
        LOG.info("File size: {}", payload.getBytes().length);
        LOG.info("File {} successfully uploaded", metadataMap.get("filename"));
        return ResponseEntity.ok().build();
    }

}

然后我添加了一个包含此配置的 application.yaml 文件:

spring:
  servlet:
    multipart:
      max-file-size: 2000000MB
      max-request-size: 2000000MB  
      resolve-lazily: true

我的目标是让控制器在开始读取 payload 文件之前解析并记录 metadata 文件,但 Boot 似乎忽略了 resolve-lazily 设置:控制器内的代码不会执行直到读取整个正文。

我使用下面的命令来测试控制器:

curl -F metadata=@metadata.json -F payload=@payload.bin http://localhost:8080/demo/upload

我的代码/配置有什么问题吗?我理解设置的含义了吗?

【问题讨论】:

    标签: spring-boot multipartform-data


    【解决方案1】:

    目前,如果您想避免一次读取(和缓冲)整个正文,我认为您必须提供自己的解析器,如答案here 中所述。真正有趣(但通常是不必要的)是以新的 MultipartResolver 实现的形式这样做。


    interface MultipartResolver 记录了两个现有的实现,并且都提供了一个函数setResolveLazily(boolean) (standard), (commons)。我都尝试过,但似乎都不允许独立解析或流式传输多部分文件或参数。

    默认为“false”,立即解析多部分元素,在 resolveMultipart(javax.servlet.http.HttpServletRequest) 调用时抛出相应的异常。将此设置为“true”以进行惰性多部分解析,一旦应用程序尝试获取多部分文件或参数,就会引发解析异常。

    尽管它在文档中说了什么,但我发现一旦您调用resolveMultipart,整个主体在调用返回之前都会被解析和缓冲。我知道这一点,因为我可以看到正在创建的临时文件。


    关于“我的代码有什么问题”的注释...

    回答:是的,因为通过使用@RequestParam,您间接要求 Spring 在调用控制器之前提前解析您的参数。您应该能够做的(如果文档正确)是从控制器内部独立请求参数

    配置(application.properties):

    spring.servlet.multipart.enabled = true
    spring.servlet.multipart.resolve-lazily = true
    

    控制器:

    @PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<Void> postUpload(HttpServletRequest rawRequest) {
    
        multipartResolver.setResolveLazily(true); // unclear why this is exists
        MultipartHttpServletRequest request = multipartResolver.resolveMultipart(rawRequest);
    
        String p1 = request.getParameter("first-parameter");
        String p2 = request.getParameter("second-parameter");
        System.out.println("first-parameter="+p1+", second-parameter"+p2);
    
        multipartResolver.cleanupMultipart(request);
        return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
    }
    

    我发现resolve-lazily 的一个有用方面是,它允许您为某些休息控制器编写自己的解析器,同时为其他控制器使用内置解析器(请参阅我的回答 here)。换句话说,你不必使用spring.servlet.multipart.enabled = false 来让你的解析器工作。相对于我之前看到的other advice,这是一个小小的突破。

    【讨论】:

    • 显示临时文件:find $(ls -part -d /tmp/* |grep /tomcat. |grep -v '\-docbase.' |tail --lines=1) -type f |xargs --no-run-if-empty ls -lparS -h
    猜你喜欢
    • 2012-12-29
    • 2017-12-01
    • 2012-10-22
    • 2018-12-12
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    • 2014-10-10
    • 1970-01-01
    相关资源
    最近更新 更多