将文件编码为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) 

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

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

 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将输入转换为动态的Base64和来自Apache Commons IO的 IOUtils类。

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

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

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

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

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


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

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

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

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(); 
  1. 你不是在阅读整个文件,只是前几个kb。 read方法返回实际读取的字节数。 你应该在循环中调用read ,直到它返回-1 ,以确保你已经读完了所有内容。

  2. 该文件太大,无论是它还是base64编码都适合内存。 或

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

       java -Xmx1024M YourProgram 

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

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

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

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

在applcation标签中的Manifest中写下android:largeHeap =“true”

它对我有用