我正在使用Spring Boot 1.2.5,我想将原始二进制文件上传到控制器。文件大小可能很大,所以我不想将整个请求保存在内存中,而是流式传输文件,实际上文件是作为传输开始生成的,因此客户端甚至不知道文件的大小。我看到了一个如何使用多部分编码文件上传here执行类似操作的示例。但是,我不想要多部分编码上传,只需要一个原始的字节流。我似乎无法在春天找到处理这个用例的方法。
答案 0 :(得分:4)
我想分享一些可能对某人有帮助的小发现。
我正在使用spring的MultipartFile
上传大文件,并且担心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");
}
当我用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");
}
结论,如果您只需要将文件上传到特定目录/文件,则可以使用此解决方案,它的速度将与手动流式传输文件一样快。
重要提示::有两种transferTo()
方法,一种方法接收Path
,另一种方法接收File
。不要使用收到Path
的文件,因为它会复制文件并且速度很慢。
EDIT1:
我使用HttpServletRequest
测试了该解决方案,但是除非您设置spring config spring.servlet.multipart.enabled = false
,否则它仍将存储一个临时文件。使用MultipartHttpServletRequest
的解决方案也会发生同样的情况。
使用所发现的解决方案,我看到了三个主要好处:
@RequestPart MultipartFile
public ResponseEntity<?> uploadFile(@RequestPart @Valid MyCustomPOJO pojo, @RequestPart MultipartFile file1, @RequestPart MultipartFile file2, @RequestPart MultipartFile file3)
这是我为测试某些概念而创建的测试项目的URL,包括以下内容:
答案 1 :(得分:2)
您可以使用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"));
}