无法让Spring Boot延迟解析多部分文件 [英] Cannot get Spring Boot to lazily resolve a multipart file
问题描述
我已经使用Spring Initializr创建了一个Spring Boot 2演示应用程序,并在下面添加了控制器:
I have created a Spring Boot 2 demo application with the Spring Initializr and added the controller below:
@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文件:
I then added an application.yaml file containing this configuration:
spring:
servlet:
multipart:
max-file-size: 2000000MB
max-request-size: 2000000MB
resolve-lazily: true
我的目标是让控制器解析并记录metadata
文件,然后再开始读取payload
文件,但是resolve-lazily
设置似乎被Boot忽略:控制器内部的代码不会执行直到读取整个身体.
My goal is to have the controller parse and log the metadata
file before it starts reading the payload
file, but the resolve-lazily
setting seems to be ignored by Boot: the code inside the controller won't be executed until the whole body is read.
我使用下面的命令来测试控制器:
I use the command below to test the controller:
curl -F metadata=@metadata.json -F payload=@payload.bin http://localhost:8080/demo/upload
我的代码/配置有什么问题吗?我能正确理解设置的含义吗?
Is there anything wrong with my code/configuration? Am I getting the meaning of the setting right?
推荐答案
目前,如果您想避免一次读取(并缓冲)整个身体,我想您将必须提供自己的解析器,如此处中所述.真正有趣的(但通常是不必要的)是以新的MultipartResolver实现的形式实现的.
At present, if you want to avoid reading (and buffering) the whole body all at once, I think you will have to provide your own parser, as described in the answers here. What would be really interesting (but generally unnecessary) would be to do so in the form of a new MultipartResolver implementation.
有两个针对接口MultipartResolver ,并且都提供了功能setResolveLazily(boolean)
(常见).我已经尝试过两者,但似乎都不允许独立解析或流式传输多部分文件或参数.
There are two existing implementations documented for interface MultipartResolver, and both supply a function setResolveLazily(boolean)
(standard), (commons). I have tried with both, and neither seem to allow for parsing or streaming multipart files or parameters independently.
默认为"false",立即解析multipart元素,并在resolveMultipart(javax.servlet.http.HttpServletRequest)调用时引发相应的异常.将其切换为"true"以进行延迟的多部分解析,一旦应用程序尝试获取多部分文件或参数,则抛出解析异常.
Default is "false", resolving the multipart elements immediately, throwing corresponding exceptions at the time of the resolveMultipart(javax.servlet.http.HttpServletRequest) call. Switch this to "true" for lazy multipart parsing, throwing parse exceptions once the application attempts to obtain multipart files or parameters.
尽管在文档中说了什么,但我发现一旦调用resolveMultipart
,就会在调用返回之前对整个正文进行解析和缓冲.我知道这一点是因为我可以观看正在创建的临时文件.
Despite what it says in the documentation, I have found that once you call resolveMultipart
, the entire body is parsed and buffered before the call returns. I know this because I can watch the temp-files being created.
关于我的代码有什么问题"的注释...
One note about "Is there anything wrong with my code"...
答案:是的,因为通过使用@RequestParam
,您曾间接要求Spring在调用控制器之前提前解析参数.相反,如果文档正确,您应该能够做的是从控制器内部独立地请求参数:
Answer: Yes, because by using @RequestParam
you have indirectly asked Spring to resolve your parameters ahead of time, before your controller is ever called. What you should be able to do instead (if the documentation were correct) is request the parameters independently from inside your controller:
配置(application.properties):
Configuration (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控制器编写自己的解析器,而对其他控制器使用内置解析器(请参阅我的答案其他建议,这是一个小突破.
One useful aspect of resolve-lazily
that I have discovered is that it allows you to write your own parser for some rest controllers while using the built-in parser for others (see my answer here). In other words, you don't have to use spring.servlet.multipart.enabled = false
to get your parser to work. This is a minor breakthrough relative to other advice that I had seen previously.
这篇关于无法让Spring Boot延迟解析多部分文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!