7

データベースにいくつかのCLOB列があり、Base64でエンコードされたバイナリファイルを配置する必要があります。これらのファイルは大きくなる可能性があるため、ストリーミングする必要があります。一度にすべてを読み取ることはできません。

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

上記のコードを実行すると、更新の実行中にこれらの1つが取得 java.io.IOException: Underlying input stream returned zero bytesされ、InputStreamReaderコードの奥深くにスローされます。

なぜこれが機能しないのですか?readerファイルストリームから読み取るベース64ストリームから読み取ろうとするように思えますが、すべてが満足できるはずです。

4

3 に答える 3

14

これはのバグのよう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]);
      }
    }
  }
}

read(byte[])呼び出しはInputStream0を返すことはできません。3バイトの倍数の長さのファイルでは0を返します。

于 2010-05-30T03:34:54.603 に答える
4

Base64InputStream興味深いことに、ここでいくつかのテストを行いました。ストリームのソースに関係なく、を使用して読み取ると、実際にその例外がスローされますがInputStreamReader、バイナリストリームとして読み取ると問題なく動作します。Trashgodが述べたように、Base64エンコーディングはフレーム化されています。は、実際には、それ以上データが返されないかどうかを確認するためにもう一度InputStreamReader呼び出しflush()ているはずです。Base64InputStream

Base64InputStreamReader独自のまたはを実装する以外に、これを修正する方法はありませんBase64Readerこれは実際にはバグです。キースの回答を参照してください。

回避策として、DBのCLOBではなくBLOBに格納し、代わりに使用することもできますPreparedStatement#setBinaryStream()。バイナリデータとして保存されているかどうかは関係ありません。とにかく、このような大きなBase64データをインデックス作成または検索できるようにする必要はありません。


更新:これはオプションではなく、Apache Commons Codecの担当者に、 CODEC-101Base64InputStreamとして再調査したバグの修正に時間がかかる可能性があるため、別のサードパーティのBase64APIの使用を検討してください。私はここでそれを見つけました(パブリックドメインなので、あなたはそれを好きなように使うことができ、あなた自身のパッケージに入れることさえできます)、私はここでそれをテストしました、そしてそれはうまくいきます。

InputStream base64 = new Base64.InputStream(input, Base64.ENCODE);

アップデート2:コモンズコーデックの担当者がすぐに修正しました。

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

ここで試してみましたが、問題なく動作します。

于 2010-05-30T03:43:09.183 に答える
0

「最高の効率を得るには、InputStreamReader内をラップすることを検討してBufferedReaderください。例:」

BufferedReader in = new BufferedReader(new InputStreamReader(b64is));

補遺:Base644文字の倍数に埋め込まれているように、ソースが切り捨てられていないことを確認します。Aflush()が必要な場合があります。

于 2010-05-30T01:41:58.057 に答える