Android文件转Base64时使用流方式有时会漏掉2个字节。

7
我写信是想寻求解决以下问题的正确方法:
我需要将文件编码为Base64格式,但是由于文件无法缩小,因此我肯定会遇到OutOfMemory异常,所以我使用了流式处理来解决它。在文件被编码之后,我立即通过代码和在线工具对其进行了解码。有时候发现解码后的内容末尾丢失了2个字节,但并非总是如此。这确实影响了文件的进一步处理。
希望有人能提供帮助,也许是由于愚蠢的错误导致的。还是要感谢。
以下是代码:
FileOutputStream fout = new FileOutputStream(path + ".txt");
//this is for printing out the base64 content
FileInputStream fin = new FileInputStream(path);

System.out.println("File Size:" + path.getTotalSpace());

ByteArrayOutputStream os = new ByteArrayOutputStream();
Base64OutputStream base64out = new Base64OutputStream(os,Base64.NO_WRAP);

byte[] buffer = new byte[3 * 512];
int len = 0;
while ((len = fin.read(buffer)) >= 0) {
    base64out.write(buffer, 0, len);
}

System.out.println("Encoded Size:" + os.size());

String result = new String(os.toByteArray(), "UTF-8");

fout.write(os.toByteArray());
fout.close();
base64out.close();
os.close();
fin.close();

return result;

在关闭输出流之前,您可能需要flush()一下输出流。 - Dawnkeeper
@Dawnkeeper 你的意思是在关闭输出流之前添加<var>.flush()吗? 因此代码应该是: fout.flush(); fout.close(); base64out.flush(); base64out.close(); os.flush(); os.close(); - Frank Fung
2个回答

4
这是在使用Base64OutputStream时有效的解决方案。我将在代码后面进行一些解释:
以下是代码:
FileOutputStream fout = new FileOutputStream(path + ".txt");
FileInputStream fin = new FileInputStream(path);

System.out.println("File Size:" + path.length());

ByteArrayOutputStream os = new ByteArrayOutputStream();
Base64OutputStream base64out = new Base64OutputStream(os,Base64.NO_WRAP);

byte[] buffer = new byte[3 * 512];
int len = 0;
while ((len = fin.read(buffer)) >= 0) {
    base64out.write(buffer, 0, len);
}

System.out.println("Encoded Size:" + os.size());

base64out.flush();
base64out.close();//this did the tricks. Please see explanation.

String result = new String(os.toByteArray(), "UTF-8");      

fout.write(os.toByteArray());
fout.flush();
fout.close();
os.close();
fin.close();

return result;

事实上,使用Dawnkeeper建议的添加flush()后,我只移动了一行代码。 诀窍在于在处理任何数据之前执行base64out.close()。关闭Base64OutputStream可能会将Base64的结束填充写入输出。 我从org.apache.myfaces.trinidad.util.Base64OutputStream close()方法中得到了我的想法。这就是为什么我尝试的原因。
在数据处理之前关闭OutputStream可能看起来很奇怪,但在这种情况下它只关闭Base64OutputStream而不是ByteArrayOutputStream。因此,数据仍然可以从ByteArrayOutputStream中检索。
感谢Dawnkeeper的努力,并希望这个问题能够帮助其他遭受OutOfMemory异常困扰的人。

代码块并不被视为解决方案,您应该描述它是如何解决您的问题的。 - Maarten Bodewes
我已经在问题中进行了解释。这个解决方案只是为了关闭话题。 - Frank Fung
请勿在问题中提供答案。相反,将问题中的解释复制到答案中,并将答案从问题中删除。 - Maarten Bodewes

0

在进行其他操作和关闭流之前,应该先刷新您的流:

FileOutputStream fout = new FileOutputStream(path + ".txt");
//this is for printing out the base64 content
FileInputStream fin = new FileInputStream(path);
System.out.println("File Size:" + path.length());   // path.getTotalSpace() is the filesystem size
ByteArrayOutputStream os = new ByteArrayOutputStream();
Base64OutputStream base64out = new Base64OutputStream(os,Base64.NO_WRAP);

byte[] buffer = new byte[3 * 512];
int len = 0;
while ((len = fin.read(buffer)) >= 0) {
    base64out.write(buffer, 0, len);
}
System.out.println("Encoded Size:" + os.size());

base64out.write(System.lineSeparator().getBytes());   
base64out.flush();  // to be sure that everything is in the byte array
String result = new String(os.toByteArray(), "UTF-8");
fout.write(os.toByteArray());
fout.flush();  // make sure everything is written
fout.close();
base64out.close();
os.close();
fin.close();

System.out.println("new File Size:" + new File(path + ".txt").length()); // prints out the size of the finished file

return result;

我通过循环运行代码,一遍又一遍地转换一个2MB的文件,但是从未得到过大小差异。此外,我修改了代码以修复输出时使用的错误长度。 - Dawnkeeper
是的,我看到了,但它并没有帮助。我已经尝试了你的代码,但仍然存在大小差异。此外,正如我所提到的,它有时只会发生,而不总是发生。我已经尝试了你的答案对于10个不同的文件,但只有5个成功了。 - Frank Fung
是否总是同一些文件失败?如果是,那么与成功的文件有什么不同之处? - Dawnkeeper
似乎流需要在末尾加上换行符才能正常工作。我已经更改了代码以适应这一点。您将不得不测试它是否会影响之前工作的文件。如果是,您必须检测文件末尾的换行符并在需要时添加它。最后,您确定 Base64.NO_WRAP 被正确命名和使用,因为它可以打开和关闭编码吗? - Dawnkeeper
并不总是相同的文件失败,而是随机的,无法指定。再次感谢您的建议,我终于找到了解决方案,而不需要在末尾编写行分隔符。此外,我非常确定Base64.NO_WRAP被正确使用,因为它应该省略行终止符以避免行太长,但我只能允许它没有行终止符。在删除一些虚拟代码后,我将更新我的问题。再次感谢。 - Frank Fung

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接