无法使Spring Boot延迟解析多部分文件

4

我使用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文件,但是resolve-lazily设置似乎被Boot忽略了:控制器内部的代码直到整个body被读取才会执行。
我使用以下命令测试控制器:
curl -F metadata=@metadata.json -F payload=@payload.bin http://localhost:8080/demo/upload
我的代码/配置有什么问题吗?我对设置的含义有没有理解正确?
1个回答

2
目前,如果你想避免一次性阅读(和缓冲)整个正文,我认为你将不得不提供自己的解析器,就像这里的答案中描述的那样。但真正有趣的事情(但通常是不必要的)是以新的MultipartResolver实现的形式来完成解析。

MultipartResolver接口有两个已知的实现文档,它们都提供了一个setResolveLazily(boolean)函数 (standard), (commons)。我尝试使用了两种实现方式,但都似乎不允许独立解析或流式处理multipart文件或参数。

默认为"false",立即解决multipart元素,在resolveMultipart(javax.servlet.http.HttpServletRequest)调用时抛出相应的异常。将其切换为"true"可以进行惰性的multipart解析,在应用程序尝试获取multipart文件或参数时抛出解析异常。

尽管文档中所说的是一旦调用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 的一个有用方面是,它允许您为一些 REST 控制器编写自己的解析器,同时使用内置解析器来处理其他控制器(请参见我的答案这里)。换句话说,您不必使用 spring.servlet.multipart.enabled = false 来使您的解析器工作。与我之前看到的其他建议相比,这是一个小突破。

显示临时文件:find $(ls -part -d /tmp/* |grep /tomcat. |grep -v '\-docbase.' |tail --lines=1) -type f |xargs --no-run-if-empty ls -lparS -h - Brent Bradburn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接