将文件编码为base64时内存不足

Iva*_*ich 15 java base64

使用Apache commons的Base64

public byte[] encode(File file) throws FileNotFoundException, IOException {
        byte[] encoded;
        try (FileInputStream fin = new FileInputStream(file)) {
            byte fileContent[] = new byte[(int) file.length()];
            fin.read(fileContent);
            encoded = Base64.encodeBase64(fileContent);
        }
        return encoded;   
}


Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
    at org.apache.commons.codec.binary.BaseNCodec.encode(BaseNCodec.java:342)
    at org.apache.commons.codec.binary.Base64.encodeBase64(Base64.java:657)
    at org.apache.commons.codec.binary.Base64.encodeBase64(Base64.java:622)
    at org.apache.commons.codec.binary.Base64.encodeBase64(Base64.java:604)
Run Code Online (Sandbox Code Playgroud)

我正在为移动设备制作小应用程序.

Tom*_*icz 31

您不能只将整个文件加载到内存中,如下所示:

byte fileContent[] = new byte[(int) file.length()];
fin.read(fileContent);
Run Code Online (Sandbox Code Playgroud)

而是按块加载文件块并将其编码为部分.Base64是一个简单的编码,它足以加载3个字节并一次编码(这将在编码后产生4个字节).出于性能原因,考虑加载3个字节的倍数,例如3000个字节 - 应该没问题.还要考虑缓冲输入文件.

一个例子:

byte fileContent[] = new byte[3000];
try (FileInputStream fin = new FileInputStream(file)) {
    while(fin.read(fileContent) >= 0) {
         Base64.encodeBase64(fileContent);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不能简单地将结果附加Base64.encodeBase64()encodedbbyte数组.实际上,它没有加载文件,而是将其编码为Base64导致内存不足问题.这是可以理解的,因为Base64版本更大(并且您已经拥有占用大量内存的文件).

考虑将您的方法更改为:

public void encode(File file, OutputStream base64OutputStream)
Run Code Online (Sandbox Code Playgroud)

并将Base64编码的数据直接发送给base64OutputStream而不是返回它.

更新:感谢@StephenC我开发了更简单的版本:

public void encode(File file, OutputStream base64OutputStream) {
  InputStream is = new FileInputStream(file);
  OutputStream out = new Base64OutputStream(base64OutputStream)
  IOUtils.copy(is, out);
  is.close();
  out.close();
}
Run Code Online (Sandbox Code Playgroud)

它采用Base64OutputStream的是输入转换为Base64 的即时IOUtils类从Apache的百科全书IO.

注意:如果需要,您必须关闭FileInputStreamBase64OutputStream显式打印=,但缓冲由IOUtils.copy().处理.


Sor*_*rin 5

好吧,不要一次对整个文件执行此操作。

Base64 一次处理 3 个字节,因此您可以批量读取“3 个”字节的文件,对它们进行编码并重复,直到完成文件:

// the base64 encoding - acceptable estimation of encoded size
StringBuilder sb = new StringBuilder(file.length() / 3 * 4);

FileInputStream fin = null;
try {
    fin = new FileInputStream("some.file");
    // Max size of buffer
    int bSize = 3 * 512;
    // Buffer
    byte[] buf = new byte[bSize];
    // Actual size of buffer
    int len = 0;

    while((len = fin.read(buf)) != -1) {
        byte[] encoded = Base64.encodeBase64(buf);

        // Although you might want to write the encoded bytes to another 
        // stream, otherwise you'll run into the same problem again.
        sb.append(new String(buf, 0, len));
    }
} catch(IOException e) {
    if(null != fin) {
        fin.close();
    }
}

String base64EncodedFile = sb.toString();
Run Code Online (Sandbox Code Playgroud)


Ste*_*n C 5

文件太大,或者堆太小,或者你有内存泄漏.

  • 如果这只发生在非常大的文件中,请在代码中添加一些内容来检查文件大小并拒绝不合理的大文件.

  • 如果小文件发生这种情况,请在启动JVM时使用-Xmx命令行选项增加堆大小.(如果这是在Web容器或其他框架中,请查看有关如何执行此操作的文档.)

  • 如果文件重复出现,特别是对于小文件,则可能是内存泄漏.


另一点应该是你当前的方法需要在内存中保存两个完整的文件副本.您应该能够减少内存使用量,但通常需要基于流的Base64编码器才能执行此操作.(这取决于您使用的base64编码的风格......)

此页面描述了基于流的Base64编码器/解码器库,并包含一些替代方案的lnks.