我在控制器中有一个简单的方法,从数据库中流内容,流按预期工作,在调用端点后立即开始下载。问题是堆的使用,流256 MB文件占用1GB堆空间。如果我用将数据从本地文件读取到输入流并复制到传递的输出流的方法替换service.writeContentToStream(param1, param2, out)
,则结果是相同的。我能流的最大文件是256 MB。是否有可能克服堆大小限制的解决方案?
@GetMapping("/{param1}/download-stream")
public ResponseEntity<StreamingResponseBody> downloadAsStream(
@PathVariable("param1") String param1,
@RequestParam(value = "param2") String param2
) {
Metadata metadata = service.getMetadata(param1);
StreamingResponseBody stream = out -> service.writeContentToStream(param1, param2, out);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;" + getFileNamePart() + metadata.getFileName())
.header(HttpHeaders.CONTENT_LENGTH, Long.toString(metadata.getFileSize()))
.body(stream);
}
service.writeContentToStream
法
try (FileInputStream fis = new FileInputStream(fileName)) {
StreamUtils.copy(fis, dataOutputStream);
} catch (IOException e) {
log.error("Error writing file to stream",e);
}
类只包含有关文件大小和文件名的信息,其中没有存储任何内容。
编辑实现的StreamUtils.copy()方法,它来自弹簧库StreamUtils.copy()。缓冲区大小设置为4096。将缓冲区设置为较小的大小不允许我下载更大的文件。
/**
* Copy the contents of the given InputStream to the given OutputStream.
* Leaves both streams open when done.
* @param in the InputStream to copy from
* @param out the OutputStream to copy to
* @return the number of bytes copied
* @throws IOException in case of I/O errors
*/
public static int copy(InputStream in, OutputStream out) throws IOException {
Assert.notNull(in, "No InputStream specified");
Assert.notNull(out, "No OutputStream specified");
int byteCount = 0;
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
byteCount += bytesRead;
}
out.flush();
return byteCount;
}
发布于 2022-03-02 09:08:23
对我来说,这是日志依赖关系,因此,如果您在识别堆使用原因方面遇到问题,请查看日志记录配置:
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-spring-boot-starter</artifactId>
<version>1.4.1</version>
<scope>compile</scope>
</dependency>
发布于 2021-12-29 11:55:39
一些想法:
ServletResponse.setBufferSize(...)
发布于 2021-12-29 12:17:24
早在2016年,我就写了一篇关于文章的文章,那是关于StreamingResponseBody
的第一次发行。你可以通过阅读来获得更多的想法。但是,即使没有这一点,您使用下面的代码所做的工作也是不可扩展的(想象一下,100个用户同时尝试下载)。
try (FileInputStream fis = new FileInputStream(fileName)) {
StreamUtils.copy(fis, dataOutputStream);
} catch (IOException e) {
log.error("Error writing file to stream",e);
}
上面的代码非常占用内存,而具有高内存的节点只能处理这个问题,而且文件大小总是有一个上限(它能在5年内下载一个1TB文件吗?)
你应该做的是以下几点;
try (FileInputStream fis = new FileInputStream(fileName)) {
byte[] data = new byte[2048];
int read = 0;
while ((read = fis.read(data)) > 0) {
dataOutputStream.write(data, 0, read);
}
dataOutputStream.flush();
} catch (IOException e) {
log.error("Error writing file to stream",e);
}
这样,您的代码就可以下载任意大小的文件,因为用户可以等待,并且不需要大量内存。
https://stackoverflow.com/questions/70513776
复制相似问题