请帮助我找到使用 Spring MVC 实现下载大文件的端点的正确方法。
我知道涉及显式写入
HttpServletResponse
输出流的方法:
@PostMapping("/download")
void downloadFile(
@PathVariable("path") String path,
HttpServletResponse response
) throws Exception {
try (var output = response.getOutputStream()) {
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
service.readFilePart(path, output));
}
}
但是,一个缺点是,当
service.readFilePart
方法引发异常时,端点仍然返回 HTTP 200,而不是带有错误状态和一些 JSON 的响应。
我找到了几种方法,但它们都建议在发送响应之前收集内存中的所有文件数据,例如:
try (var output = new ByteArrayOutputStream()) {
service.readFilePart(path, output));
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(output.toByteArray());
}
我正在寻找一种解决方案,可以处理大文件,而无需将大于 1MB 的块加载到内存中,同时还可以利用标准 Spring MVC 功能(例如异常处理程序)。
似乎使用
HttpServletResponse
参数可能会破坏抽象级别。是否有可能在Spring MVC中使用更高级别的抽象来实现类似的功能?
@GetMapping("/download")
public ResponseEntity<StreamingResponseBody> downloadFile(@RequestParam("path") String path) {
try {
// Assuming you have a method to get the file size
long fileSize = fileService.getFileSize(path);
StreamingResponseBody stream = outputStream -> {
try {
fileService.readFilePart(path, outputStream);
} catch (IOException e) {
throw new RuntimeException("Failed to write file", e);
}
};
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + extractFilename(path) + "\"")
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileSize)) // Set the content length
.body(stream);
} catch (Exception e) {
// Log the exception and return an appropriate error response
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(null); // You can customize the response as needed
}
}