Spring上传非多部分文件作为流

cma*_*ard 11 spring file-upload binaryfiles spring-boot

我正在使用 Spring Boot 1.2.5,我想将原始二进制文件上传到控制器。文件大小可能很大,所以我不想将整个请求保存在内存中,而是流式传输文件,实际上文件是在传输开始时生成的,因此客户端甚至不知道文件的大小。我在此处看到了一个示例,说明如何使用多部分编码文件上传执行类似操作。但是,我不想要分段编码的上传,只需要原始字节流。我似乎找不到在春季处理这个用例的方法。

Mag*_*nus 9

您可以只使用输入HttpServletRequest流。
请注意,如果您有任何过滤器来预处理请求并使用输入流,那么这可能不起作用。

@ResponseBody
@RequestMapping(path="fileupload", method = RequestMethod.POST, consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void fileUpload(HttpServletRequest request) throws IOException {
    Files.copy(request.getInputStream(), Paths.get("myfilename"));
}
Run Code Online (Sandbox Code Playgroud)


Lui*_*ang 7

我想分享一些可能对某人有所帮助的小发现。

我正在使用 springMultipartFile上传大文件,并担心 spring 会将内容存储在内存中。因此,我决定使用该getInputStream()方法,希望这可以将文件直接流式传输到所需位置:

@PostMapping("/upload")
    public ResponseEntity<?> uploadFile(@RequestPart MultipartFile file) throws FileNotFoundException, IOException{

    FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(new File("/storage/upload/", file.getOriginalFilename())));

    return ResponseEntity.ok("Saved");
}
Run Code Online (Sandbox Code Playgroud)

当我使用 2GB 文件测试控制器时,需要很长时间才能命中控制器方法。于是调试了一下,发现spring/Tomcat先把文件存放在一个临时文件夹中,然后再处理给控制器。这意味着,当您调用getInputStream()它时,它会返回FileInputStream指向存储在文件系统上的文件,而不是直接从客户端浏览器流式传输。

换句话说,调用FileCopyUtils.copy()很慢,因为它将整个文件复制到另一个位置,然后删除临时文件,使得完成请求需要两倍的时间。

我调查并发现您可以禁用 spring 功能并手动处理多部分请求,但有点复杂且容易出错。所以,再挖掘一点,我发现MultipartFile有一个方法被调用transferTo,实际上将临时文件移动到所需的位置。我测试了它,它是即时的。我的代码是这样的:

@PostMapping("/upload")
public ResponseEntity<?> uploadFile(@RequestPart MultipartFile file) throws FileNotFoundException, IOException{

    file.transferTo(new File("/storage/upload/", file.getOriginalFilename()));

    return ResponseEntity.ok("Saved");
}
Run Code Online (Sandbox Code Playgroud)

结论,如果您只想将文件上传到特定目录/文件,您可以使用此解决方案,它与手动流式传输文件一样快。

重要提示:有两种transferTo()方法,一种接收 a Path,另一种接收File. 不要使用接收 a 的那个,Path因为它会复制文件并且速度很慢。

编辑1:

我使用 测试了该解决方案HttpServletRequest,但除非您设置 spring config ,否则它仍将存储一个临时文件spring.servlet.multipart.enabled = false。使用 的解决方案也会发生同样的情况MultipartHttpServletRequest

使用我发现的解决方案,我看到了三个主要好处:

  1. 这很简单
  2. 它允许您一次处理多个文件,您只需要@RequestPart MultipartFile在控制器方法中添加多个文件
  3. 它允许您轻松地处理包含文件的响应正文
public ResponseEntity<?> uploadFile(@RequestPart @Valid MyCustomPOJO pojo, @RequestPart MultipartFile file1, @RequestPart MultipartFile file2, @RequestPart MultipartFile file3)
Run Code Online (Sandbox Code Playgroud)

这是我为测试一些概念而创建的测试项目的 URL,包括这个:

https://github.com/noschang/SpringTester