проблема с помощью base64 кодер и InputStreamReader

У меня есть некоторые столбцы 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);

Когда я выполняю вышеупомянутый код, я получаю один из них во время выполнения обновления java.io.IOException: Underlying input stream returned zero bytes, это брошено глубоко в коде InputStreamReader.

Почему это не работало бы? Это кажется мне как reader попытался бы считать из основы 64 потока, которые будут читать из потока файла, и все должно быть счастливым.

7
задан karoberts 30 May 2010 в 02:30
поделиться

3 ответа

Похоже, это ошибка в Base64InputStream . Вы правильно называете это.

Вы должны сообщить об этом проекту кодеков Apache Commons.

Простой тестовый пример:

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 []) для InputStream не может возвращать 0. Он возвращает 0 для любого файла, который является кратным длиной 3 байта.

14
ответ дан 6 December 2019 в 10:47
поделиться

Интересно, я провел здесь несколько тестов, и это действительно вызывает это исключение, когда вы читаете Base64InputStream с помощью InputStreamReader , независимо от источника потока, но он работает безупречно, когда вы прочтите его как двоичный поток. Как упоминал Trashgod, кодировка Base64 оформлена. InputStreamReader фактически должен был вызвать flush () в Base64InputStream еще раз, чтобы проверить, не возвращает ли он больше данных.

Я не вижу других способов исправить это, кроме реализации собственного Base64InputStreamReader или Base64Reader . На самом деле это ошибка, см. Ответ Кита.

В качестве обходного пути вы также можете просто сохранить его в BLOB вместо CLOB в базе данных и вместо этого использовать PreparedStatement # setBinaryStream () . Не имеет значения, хранятся ли они как двоичные данные или нет. В любом случае вы не хотите, чтобы такие большие данные Base64 можно было индексировать или искать.


Обновление : поскольку это не вариант, и наличие ребят из Apache Commons Codec для исправления ошибки Base64InputStream , которую я представил как CODEC-101 , может занять некоторое время, вы может рассмотреть возможность использования стороннего API Base64. Я нашел один здесь (общественное достояние, так что вы можете делать с ним все, что хотите, даже помещать в свой собственный пакет), я протестировал его здесь, и он отлично работает.

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

Я попробовал это здесь, и он отлично работает.

4
ответ дан 6 December 2019 в 10:47
поделиться

"Для максимальной эффективности рассмотрите возможность обертывания InputStreamReader внутри BufferedReader. Например:"

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

Дополнение: Поскольку Base64 заполняется до числа, кратного 4 символам, убедитесь, что источник не усечен. Может потребоваться flush().

0
ответ дан 6 December 2019 в 10:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: