首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >StreamingResponseBody堆使用

StreamingResponseBody堆使用
EN

Stack Overflow用户
提问于 2021-12-28 23:52:15
回答 3查看 410关注 0票数 2

我在控制器中有一个简单的方法,从数据库中流内容,流按预期工作,在调用端点后立即开始下载。问题是堆的使用,流256 MB文件占用1GB堆空间。如果我用将数据从本地文件读取到输入流并复制到传递的输出流的方法替换service.writeContentToStream(param1, param2, out),则结果是相同的。我能流的最大文件是256 MB。是否有可能克服堆大小限制的解决方案?

代码语言:javascript
运行
复制
    @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

代码语言:javascript
运行
复制
 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。将缓冲区设置为较小的大小不允许我下载更大的文件。

代码语言:javascript
运行
复制
    /**
     * 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;
    }
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-03-02 09:08:23

对我来说,这是日志依赖关系,因此,如果您在识别堆使用原因方面遇到问题,请查看日志记录配置:

代码语言:javascript
运行
复制
  <dependency>
        <groupId>org.zalando</groupId>
        <artifactId>logbook-spring-boot-starter</artifactId>
        <version>1.4.1</version>
        <scope>compile</scope>
  </dependency>
票数 0
EN

Stack Overflow用户

发布于 2021-12-29 11:55:39

一些想法:

  1. 在Java分析器中运行服务器。例如,JProfiler (它要花钱)。
  2. 试试ServletResponse.setBufferSize(...)
  3. 检查是否在应用程序中配置了一些筛选器。
  4. 检查应用程序服务器的输出缓冲区。如果是Tomcat的话,这可能会很棘手。它有一长串可能的缓冲区:

https://tomcat.apache.org/tomcat-8.5-doc/config/http.html

票数 0
EN

Stack Overflow用户

发布于 2021-12-29 12:17:24

早在2016年,我就写了一篇关于文章的文章,那是关于StreamingResponseBody的第一次发行。你可以通过阅读来获得更多的想法。但是,即使没有这一点,您使用下面的代码所做的工作也是不可扩展的(想象一下,100个用户同时尝试下载)。

代码语言:javascript
运行
复制
 try (FileInputStream fis = new FileInputStream(fileName)) {
     StreamUtils.copy(fis, dataOutputStream);
 } catch (IOException e) {
     log.error("Error writing file to stream",e);
 }

上面的代码非常占用内存,而具有高内存的节点只能处理这个问题,而且文件大小总是有一个上限(它能在5年内下载一个1TB文件吗?)

你应该做的是以下几点;

代码语言:javascript
运行
复制
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);
}

这样,您的代码就可以下载任意大小的文件,因为用户可以等待,并且不需要大量内存。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70513776

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档