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

时间:2012-03-06 08:00:33

标签: 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)

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

8 个答案:

答案 0 :(得分:30)

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

byte fileContent[] = new byte[(int) file.length()];
fin.read(fileContent);

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

一个例子:

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

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

考虑将您的方法更改为:

public void encode(File file, OutputStream base64OutputStream)

将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();
}

它使用Base64OutputStream将输入转换为IOUtils中的Base64 动态Apache Commons IO类。

注意:如果需要,您必须明确关闭FileInputStreamBase64OutputStream以打印=,但缓存由IOUtils.copy()处理。

答案 1 :(得分:5)

文件太大,或者堆太小,或者你的内存泄漏。

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

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

  • 如果文件再次出现,特别是对于小文件,则可能是内存泄漏。


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

This page描述了一个基于流的Base64编码器/解码器库,并包含了一些替代方案。

答案 2 :(得分:4)

好吧,不要一次为整个文件做这件事。

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();

答案 3 :(得分:1)

  1. 你没有读完整个文件,只是前几个kb。 read方法返回实际读取的字节数。您应该循环调用read,直到它返回-1,以确保您已阅读所有内容。

  2. 该文件太大,无法将其及其base64编码放入内存中。

    • 以较小的片段或
    • 处理文件
    • 使用-Xmx开关增加JVM可用的内存,例如

      java -Xmx1024M YourProgram
      

答案 4 :(得分:1)

这是上传更大尺寸图片的最佳代码

bitmap=Bitmap.createScaledBitmap(bitmap, 100, 100, true);

ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); //compress to which format you want.
byte [] byte_arr = stream.toByteArray();  
String image_str = Base64.encodeBytes(byte_arr);

答案 5 :(得分:0)

好吧,看起来您的文件太大,无法同时在可用堆内存中保留内存中Base64编码所需的多个副本。鉴于这是针对移动设备的,可能无法增加堆,因此您有两种选择:

  • 使文件更小(更小)
  • 以基于stram的方式执行此操作,以便您一次从InputStream文件的一小部分进行读取,对其进行编码并将其写入OutputStream,而无需保留内存中的enitre文件。

答案 6 :(得分:0)

在applcation标签中的Manifest中写下面的内容   机器人:largeHeap ="真"

它对我有用

答案 7 :(得分:0)

Java 8添加了Base64方法,因此不再需要Apache Commons来编码大型文件。

public static void encodeFileToBase64(String inputFile, String outputFile) {
    try (OutputStream out = Base64.getEncoder().wrap(new FileOutputStream(outputFile))) {
        Files.copy(Paths.get(inputFile), out);
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}