【问题标题】:how to download large files without memory issues in java如何在java中下载没有内存问题的大文件
【发布时间】:2011-08-18 11:36:49
【问题描述】:

当我尝试从服务器下载一个 260MB 的大文件时,我收到此错误:java.lang.OutOfMemoryError: Java heap space. 我确定我的堆大小小于 252MB。有什么方法可以在不增加堆大小的情况下下载大文件?

如何下​​载大文件而不出现此问题?我的代码如下:

String path= "C:/temp.zip";   
response.addHeader("Content-Disposition", "attachment; filename=\"test.zip\""); 
byte[] buf = new byte[1024];   
try {   

             File file = new File(path);   
             long length = file.length();   
             BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));   
             ServletOutputStream out = response.getOutputStream();   

             while ((in != null) && ((length = in.read(buf)) != -1)) {   
             out.write(buf, 0, (int) length);   
             }   
             in.close();   
             out.close();

【问题讨论】:

  • 你能增加 JVM 的堆大小吗?即:java -Xm512m -Xmx512m myClass
  • 是的,但我想知道我是否可以在不增加 jvm 堆大小的情况下做到这一点,因为我们需要下载大约 1gb 到 10gb 的大文件

标签: java out-of-memory


【解决方案1】:

我可以看到有 2 个地方可能会增加内存使用量:

  1. 在缓冲区中读取您的输入文件。
  2. 在缓冲区中写入您的输出流 (HTTPOutputStream?)

对于#1,我建议通过FileInputStream 直接从文件中读取,而不使用BufferedInputStream。先试试这个,看看能不能解决你的问题。即:

FileInputStream in = new FileInputStream(file);   

代替:

BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));   

如果 #1 不能解决问题,您可以尝试在写入这么多数据后定期刷新输出流(必要时减小块大小):

即:

try
{
    FileInputStream fileInputStream  = new FileInputStream(file);
    byte[] buf=new byte[8192];
    int bytesread = 0, bytesBuffered = 0;
    while( (bytesread = fileInputStream.read( buf )) > -1 ) {
        out.write( buf, 0, bytesread );
        bytesBuffered += bytesread;
        if (bytesBuffered > 1024 * 1024) { //flush after 1MB
            bytesBuffered = 0;
            out.flush();
        }
    }
}
finally {
    if (out != null) {
        out.flush();
    }
}

【讨论】:

  • 上面的代码有一个小问题。如果代码周围有一个 try-catch 或者在 while 循环的末尾,我们需要在 finally 块中再添加一个刷新。否则它不会刷新完整的字节。否则有时可能会导致一些问题。
  • 哇...现在完美了:)
  • 有没有办法下载1g文件?
【解决方案2】:

很遗憾,您没有提到out 是什么类型。如果你有记忆问题,我猜是ByteArrayOutpoutStream。因此,将其替换为 FileOutputStream 并将您正在下载的字节直接写入文件。

顺便说一句,不要使用read() 逐字节读取的方法。请改用read(byte[] arr)。这要快得多。

【讨论】:

  • 从“response.setContentType”和“response.setHeader”调用判断,这是一个servlet——即。他正在将文件流式传输到 Web 客户端,“out”是 HttpOutputStream。
  • 谢谢亚历克斯,但我的主要问题是下载更大的文件,可以是 1gb、10gb 大小的文件。在这里我得到内存异常。我怎样才能在不增加堆大小的情况下处理这个问题......
  • 好的,现在你已经更新了你的代码,问题更清楚了。我以为您的程序正在从远程 URL 下载文件,但您问的是相反的问题:您的 servlet 提供了下载文件的能力。所以,我认为你的问题是你没有在每个 out.write() 之后调用 out.flush(),所以你正在读取的所有字节都存储在内存中并且在你关闭输出流之前不会发送到网络。尝试冲洗,看看发生了什么。
【解决方案3】:

首先,您可以从 while 语句中删除 (in != null),这是不必要的。其次,尝试删除 BufferedInputStream 并执行以下操作:

FileInputStream in = new FileInputStream(file);

【讨论】:

    【解决方案4】:

    您显示的代码没有任何问题(在内存使用方面)。要么 servlet 容器被配置为缓冲整个响应(查看 web.xml 配置),要么内存在其他地方泄漏。

    【讨论】:

      猜你喜欢
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-17
      • 2011-06-26
      • 1970-01-01
      相关资源
      最近更新 更多