当前位置: 首页 > 知识库问答 >
问题:

用于下载大型文件的Spring boot Java堆空间

郗奇玮
2023-03-14

我正在尝试编写一个RESTAPI,允许用户下载大文件(即

ContentCachingResponseWrapper中的默认flushBuffer()方法为空。如果我尝试调用copyBodyToResponse(),它用于将数据从缓存复制到流,它工作正常,但也会关闭流。这导致只向客户端发送第一块数据。

有什么建议吗?

public void myDownloader(HttpServletRequest request, HttpServletResponse response) {
             //response.getClass() is: ContentCachingResponseWrapper  
             byte[] buffer = new byte[1048576];  // 1 MB Chunks
             FileInputStream inputStream = new FileInputStream(PATH_TO_SOME_VALID_FILE);
             int bytesRead= 0;
             ServletOutputStream outputStream = response.getOutputStream();

             while ((bytesRead = inputStream.read(buffer)) != -1) {              
                 outputStream.write(buffer, 0, bytesRead);
                 outputStream.flush();
                 response.flushBuffer();
               } 
}

我发现以下错误:

Caused by: java.lang.OutOfMemoryError: Java heap space
    at org.springframework.util.FastByteArrayOutputStream.addBuffer(FastByteArrayOutputStream.java:303) ~[spring-core-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
    at org.springframework.util.FastByteArrayOutputStream.write(FastByteArrayOutputStream.java:118) ~[spring-core-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
    at org.springframework.web.util.ContentCachingResponseWrapper$ResponseServletOutputStream.write(ContentCachingResponseWrapper.java:239) ~[spring-web-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]

共有1个答案

容飞掣
2023-03-14

这是下载文件的一种非常基本的方式,但如果你从浏览器调用它,浏览器会将其显示在屏幕上,并且可能会永远旋转(如果你问我,浏览器问题):

@RequestMapping(path = "/downloadLargeFile", method = RequestMethod.GET)
public ResponseEntity<Resource> downloadLargeFile() {
    final File file = new File("c:/large.bin");
    final FileSystemResource resource = new FileSystemResource(file);
    return ResponseEntity.ok().body(resource);
}

因此,您可以包含一些包含文件信息的标题,浏览器会将其下载到下载目录中的文件中,并且不会旋转:

@RequestMapping(path = "/downloadLargeFile2", method = RequestMethod.GET)
public ResponseEntity<Resource> downloadLargeFile2() {
    final HttpHeaders httpHeaders = new HttpHeaders();
    final File file = new File("c:/large.bin");
    final FileSystemResource resource = new FileSystemResource(file);
    httpHeaders.set(HttpHeaders.LAST_MODIFIED, String.valueOf(file.lastModified()));
    httpHeaders.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"");
    httpHeaders.set(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length()));
    return ResponseEntity.ok()
        .headers(httpHeaders)
        .contentLength(file.length())
        .contentType(MediaType.APPLICATION_OCTET_STREAM)
        .body(resource);
}

要分块响应,请使用InputStreamResources,显然Spring关闭了InputStream:

@RequestMapping(path = "/pub/storage/downloadLargeFile4", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> downloadLargeFile4()
    throws Exception {
    final HttpHeaders httpHeaders = new HttpHeaders();
    final File file = new File("c:/large.bin");
    final InputStream inputStream = new FileInputStream(file);
    final InputStreamResource resource = new InputStreamResource(inputStream);
    httpHeaders.set(HttpHeaders.LAST_MODIFIED, String.valueOf(file.lastModified()));
    httpHeaders.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"");
    httpHeaders.set(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length()));
    return ResponseEntity.ok()
        .headers(httpHeaders)
        .contentLength(file.length())
        .contentType(MediaType.APPLICATION_OCTET_STREAM)
        .body(resource);
}

进口:

import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

我还发现这个线程很有用:从Spring boot rest服务下载文件

 类似资料:
  • 我刚刚在google Drive中得到了一个500MB文件的大列表。我该如何在本地Linux机器上排队下载它们呢? 我不能把它们都压缩成一个大的下载 我无法将它们全部设置为一次下载 我不能整天都在这里小批量下载它们。 我从google drive的wget/curl大型文件这样的文档中看到,google drive的api被贬低了,我们不能wget它们。 所以我正在寻找一种从谷歌驱动器按顺序下载大

  • 当我运行下面的函数时,它下载源文件大约3分钟,然后关闭会话,即使一个1-1.6GB的文件只下载了38-41MB(变化)。 从Paramiko日志文件中可以看出,SSh连接在SFTP会话关闭时保持打开状态: DEB[20120913-10:05:00.894]Thr=1帕拉米科。运输:切换到新键...DEB[20120913-10:05:06.953]Thr=1 Paramiko。传输:重新密钥(命

  • 我有一个文件表示为块的列表,目标是下载所有块,加入并保存为一个文件。 它应该适用于大文件 应该是跨浏览器解决方案 null null null null 但我仍然无法实现我的目标与涵盖的需求… 如果有人有经验,最好的解决方案,我恳请分享它在这里。谢谢

  • 我正在下载一个歌曲文件。下面的代码(好吧,原始代码,这只是我正在做的一个示例)在Asha 310设备上运行良好。然而,在较新的Asha 501设备上,下载的文件比实际文件大得多。如果我使用512缓冲区,一个2.455.870字节的文件最终会下载2.505.215字节,而且它也不会加载。使用4096缓冲区,文件大小最终为3.342.335字节!! 发生这种情况的原因是什么?它在另一部手机上运行良好,

  • 问题内容: 我需要使用curl下载远程文件。 这是我的示例代码: 但是它不能处理大文件,因为它首先读取内存。 是否可以将文件直接流式传输到磁盘? 问题答案:

  • 我们有一个运行在Linux上的Weblogic服务器,有多个受管服务器。我所关心的托管服务器的最大堆大小为1024MB。该服务器上部署了多个应用程序。其中一个应用程序处理来自REST api的响应来下载250MB的文件。对于大于50 MB的文件,有时没有问题,但有时服务器会因OOM错误而崩溃。以下是我的代码: 由于存在内存问题,我尝试采用InpuStream方法。以下是更改后的代码: 此代码没有进