使用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)
我正在为移动设备制作小应用程序。
答案 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类。
注意:如果需要,您必须明确关闭FileInputStream
和Base64OutputStream
以打印=
,但缓存由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)
你没有读完整个文件,只是前几个kb。 read
方法返回实际读取的字节数。您应该循环调用read
,直到它返回-1
,以确保您已阅读所有内容。
该文件太大,无法将其及其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编码所需的多个副本。鉴于这是针对移动设备的,可能无法增加堆,因此您有两种选择:
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);
}
}