Java 使用base64编码器和InputStreamReader时出现问题
我在数据库中有一些CLOB列,需要将Base64编码的二进制文件放入其中。 这些文件可能很大,所以我需要对它们进行流式处理,我无法一次读取全部内容 我正在使用Java 使用base64编码器和InputStreamReader时出现问题,java,encoding,jdbc,base64,inputstreamreader,Java,Encoding,Jdbc,Base64,Inputstreamreader,我在数据库中有一些CLOB列,需要将Base64编码的二进制文件放入其中。 这些文件可能很大,所以我需要对它们进行流式处理,我无法一次读取全部内容 我正在使用org.apache.commons.codec.binary.Base64InputStream进行编码,我遇到了一个问题。我的代码基本上是这样的 FileInputStream fis = new FileInputStream(file); Base64InputStream b64is = new Base64InputStream
org.apache.commons.codec.binary.Base64InputStream
进行编码,我遇到了一个问题。我的代码基本上是这样的
FileInputStream fis = new FileInputStream(file);
Base64InputStream b64is = new Base64InputStream(fis, true, -1, null);
BufferedReader reader = new BufferedReader(new InputStreamReader(b64is));
preparedStatement.setCharacterStream(1, reader);
当我运行上述代码时,我会在执行更新时得到其中一个
java.io.IOException:底层输入流返回零字节
,它被抛出到InputStreamReader代码的深处
为什么这不起作用?在我看来,为最高效率,考虑在A内包装。例如:
附录:由于填充为4个字符的倍数,请确认源代码未被截断。可能需要使用
flush()
。这似乎是Base64InputStream
中的一个错误。你说得对
您应该将此报告给ApacheCommons编解码器项目
简单测试用例:
import java.io.*;
import org.apache.commons.codec.binary.Base64InputStream;
class tmp {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream(args[0]);
Base64InputStream b64is = new Base64InputStream(fis, true, -1, null);
while (true) {
byte[] c = new byte[1024];
int n = b64is.read(c);
if (n < 0) break;
if (n == 0) throw new IOException("returned 0!");
for (int i = 0; i < n; i++) {
System.out.print((char)c[i]);
}
}
}
}
import java.io.*;
导入org.apache.commons.codec.binary.Base64InputStream;
类tmp{
公共静态void main(字符串[]args)引发IOException{
FileInputStream fis=新的FileInputStream(args[0]);
Base64InputStream b64is=新的Base64InputStream(fis,true,-1,null);
while(true){
字节[]c=新字节[1024];
int n=b64is.read(c);
如果(n<0)断裂;
如果(n==0)抛出新IOException(“返回0!”);
对于(int i=0;i
InputStream
的read(byte[])
调用不允许返回0。在任何长度为3字节倍数的文件上,它都会返回0。有趣的是,我在这里做了一些测试,当您使用InputStreamReader
读取Base64InputStream时,它确实会抛出该异常,而不管流的源是什么,但当您将其作为二进制流读取时,它会完美地工作。正如垃圾神提到的,Base64编码是有框架的。实际上,InputStreamReader
应该再次调用Base64InputStream
上的flush()
,查看它是否没有返回更多数据
除了实现您自己的Base64InputStreamReader
或Base64Reader
之外,我看不到其他解决方法。这实际上是一个bug,参见Keith的答案
作为一种解决方法,您也可以将其存储在数据库中的BLOB而不是CLOB中,并使用PreparedStatement\setBinaryStream()
。它是否存储为二进制数据并不重要。无论如何,您都不希望有这么大的Base64数据是可索引或可搜索的
<强>更新< /强>:因为这不是一个选项,并且让Apache Calm编解码器的家伙修复<代码> Base64 IndoStuts Bug,我说这可能需要一些时间,您可以考虑使用另一个第三方Base64 API。我已经找到了一个(公共域,所以你可以用它做任何你想做的事情,甚至放在你自己的包中),我在这里测试了它,它工作得很好
InputStream base64 = new Base64.InputStream(input, Base64.ENCODE);
更新2:commons编解码器的家伙很快就拥有了它
Index: src/java/org/apache/commons/codec/binary/Base64InputStream.java
===================================================================
--- src/java/org/apache/commons/codec/binary/Base64InputStream.java (revision 950817)
+++ src/java/org/apache/commons/codec/binary/Base64InputStream.java (working copy)
@@ -145,21 +145,41 @@
} else if (len == 0) {
return 0;
} else {
- if (!base64.hasData()) {
- byte[] buf = new byte[doEncode ? 4096 : 8192];
- int c = in.read(buf);
- // A little optimization to avoid System.arraycopy()
- // when possible.
- if (c > 0 && b.length == len) {
- base64.setInitialBuffer(b, offset, len);
+ int readLen = 0;
+ /*
+ Rationale for while-loop on (readLen == 0):
+ -----
+ Base64.readResults() usually returns > 0 or EOF (-1). In the
+ rare case where it returns 0, we just keep trying.
+
+ This is essentially an undocumented contract for InputStream
+ implementors that want their code to work properly with
+ java.io.InputStreamReader, since the latter hates it when
+ InputStream.read(byte[]) returns a zero. Unfortunately our
+ readResults() call must return 0 if a large amount of the data
+ being decoded was non-base64, so this while-loop enables proper
+ interop with InputStreamReader for that scenario.
+ -----
+ This is a fix for CODEC-101
+ */
+ while (readLen == 0) {
+ if (!base64.hasData()) {
+ byte[] buf = new byte[doEncode ? 4096 : 8192];
+ int c = in.read(buf);
+ // A little optimization to avoid System.arraycopy()
+ // when possible.
+ if (c > 0 && b.length == len) {
+ base64.setInitialBuffer(b, offset, len);
+ }
+ if (doEncode) {
+ base64.encode(buf, 0, c);
+ } else {
+ base64.decode(buf, 0, c);
+ }
}
- if (doEncode) {
- base64.encode(buf, 0, c);
- } else {
- base64.decode(buf, 0, c);
- }
+ readLen = base64.readResults(b, offset, len);
}
- return base64.readResults(b, offset, len);
+ return readLen;
}
}
我在这里试过,效果很好 也许它更有效,但它并不能解决问题——您的流是否有可能被截断?IIRC,base64
被框定。问题已更新。你能详细解释一下你所说的“base64是框架的”是什么意思吗?流直接来自文件。编码流必须填充为“4个字符的整数倍”,以便解码最后一个字节;如果流被截断,这将是一个问题。上面引用的参考。@trashgod-“上面引用的参考。”。在哪里?是的,你是对的。这是Base64InputStream中的一个错误+1对于证实这一点的测试用例。报告顺便说一句,我仍然想知道我的测试文件确实是3字节长的倍数的巧合:o)哇,感谢你的确认,我必须说我很惊讶我发现了这样一个bug(无论多么不经意)。不幸的是,我不能使用BLOB,因为其中的数据有时会有文本
Index: src/java/org/apache/commons/codec/binary/Base64InputStream.java
===================================================================
--- src/java/org/apache/commons/codec/binary/Base64InputStream.java (revision 950817)
+++ src/java/org/apache/commons/codec/binary/Base64InputStream.java (working copy)
@@ -145,21 +145,41 @@
} else if (len == 0) {
return 0;
} else {
- if (!base64.hasData()) {
- byte[] buf = new byte[doEncode ? 4096 : 8192];
- int c = in.read(buf);
- // A little optimization to avoid System.arraycopy()
- // when possible.
- if (c > 0 && b.length == len) {
- base64.setInitialBuffer(b, offset, len);
+ int readLen = 0;
+ /*
+ Rationale for while-loop on (readLen == 0):
+ -----
+ Base64.readResults() usually returns > 0 or EOF (-1). In the
+ rare case where it returns 0, we just keep trying.
+
+ This is essentially an undocumented contract for InputStream
+ implementors that want their code to work properly with
+ java.io.InputStreamReader, since the latter hates it when
+ InputStream.read(byte[]) returns a zero. Unfortunately our
+ readResults() call must return 0 if a large amount of the data
+ being decoded was non-base64, so this while-loop enables proper
+ interop with InputStreamReader for that scenario.
+ -----
+ This is a fix for CODEC-101
+ */
+ while (readLen == 0) {
+ if (!base64.hasData()) {
+ byte[] buf = new byte[doEncode ? 4096 : 8192];
+ int c = in.read(buf);
+ // A little optimization to avoid System.arraycopy()
+ // when possible.
+ if (c > 0 && b.length == len) {
+ base64.setInitialBuffer(b, offset, len);
+ }
+ if (doEncode) {
+ base64.encode(buf, 0, c);
+ } else {
+ base64.decode(buf, 0, c);
+ }
}
- if (doEncode) {
- base64.encode(buf, 0, c);
- } else {
- base64.decode(buf, 0, c);
- }
+ readLen = base64.readResults(b, offset, len);
}
- return base64.readResults(b, offset, len);
+ return readLen;
}
}