代码之家  ›  专栏  ›  技术社区  ›  karoberts

使用base64编码器和InputStreamReader时出现问题

  •  7
  • karoberts  · 技术社区  · 16 年前

    我在数据库中有一些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

    3 回复  |  直到 16 年前
        1
  •  14
  •   Keith Randall    16 年前

    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[]) 召唤 InputStream

        2
  •  4
  •   BalusC    16 年前

    有趣的是,我在这里做了一些测试,当您阅读 Base64InputStream 使用 InputStreamReader ,不管流的源是什么,但当您将其作为二进制流读取时,它的工作方式是完美的。正如垃圾神提到的,Base64编码是有框架的。这个 输入流阅读器 实际上应该调用 flush() 上 Base64输入流

    我认为除了实现你自己的方法之外,没有其他方法可以解决这个问题 Base64InputStreamReader 或 Base64Reader 这实际上是一个错误,见基思的答案。

    作为一种解决方法,您还可以将其存储在BLOB中,而不是DB中的CLOB中,并使用 PreparedStatement#setBinaryStream() 相反。它是否存储为二进制数据并不重要。无论如何,您不希望有这么大的Base64数据是可索引或可搜索的。


    Base64输入流 CODEC-101 可能需要一些时间,您可以考虑使用另一个第三方Base64 API。我找到了一个 here (公共域,所以你可以用它做任何你想做的事,甚至放在你自己的包里),我在这里测试过它,它工作得很好。

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

    更新2 :commons编解码器的家伙 fixed 很快就好了。

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

    我在这里试过,效果很好。

        3
  •  0
  •   trashgod    16 年前

    “为了获得最高效率,请考虑包装 InputStreamReader 在一个 BufferedReader . 例如:“

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

    附录:As Base64 填充为4个字符的倍数,请验证源未被截断。A flush()