Java8 Base64解码器抛出的非法参数异常

5

我想使用Base64.java来编码和解码文件。 Encode.wrap(InputStream)decode.wrap(InputStream) 能够实现功能,但速度较慢。因此我使用了以下代码。

public static void decodeFile(String inputFileName,
        String outputFileName)
        throws FileNotFoundException, IOException {

    Base64.Decoder decoder = Base64.getDecoder();
    InputStream in = new FileInputStream(inputFileName);
    OutputStream out = new FileOutputStream(outputFileName);

    byte[] inBuff = new byte[BUFF_SIZE];  //final int BUFF_SIZE = 1024;
    byte[] outBuff = null;
    while (in.read(inBuff) > 0) {
        outBuff = decoder.decode(inBuff);
        out.write(outBuff);
    }
    out.flush();
    out.close();
    in.close();
}

然而,它总是抛出异常。
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Input byte array has wrong 4-byte ending unit
    at java.util.Base64$Decoder.decode0(Base64.java:704)
    at java.util.Base64$Decoder.decode(Base64.java:526)
    at Base64Coder.JavaBase64FileCoder.decodeFile(JavaBase64FileCoder.java:69)
    ...

在我将final int BUFF_SIZE = 1024;更改为final int BUFF_SIZE = 3 * 1024;之后,代码可以运行。由于“BUFF_SIZE”还用于编码文件,我相信编码的文件存在问题(1024%3 = 1,这意味着文件中间添加了填充)。
此外,正如@Jon Skeet和@Tagir Valeev所提到的,我不应该忽略InputStream.read()的返回值。因此,我修改了以下代码。
(但是,我必须提到的是,该代码的运行速度比使用wrap()要快得多。我注意到速度差异是因为在jdk8发布之前,我已编写并密集使用Base64.encodeFile()/decodeFile()。现在,我的buffed jdk8代码运行速度与最初的代码一样快。所以,我不知道wrap()出了什么问题...)
public static void decodeFile(String inputFileName,
        String outputFileName)
        throws FileNotFoundException, IOException
{

    Base64.Decoder decoder = Base64.getDecoder();
    InputStream in = new FileInputStream(inputFileName);
    OutputStream out = new FileOutputStream(outputFileName);

    byte[] inBuff = new byte[BUFF_SIZE];
    byte[] outBuff = null;
    int bytesRead = 0;
    while (true)
    {
        bytesRead = in.read(inBuff);
        if (bytesRead == BUFF_SIZE)
        {
            outBuff = decoder.decode(inBuff);
        }
        else if (bytesRead > 0)
        {
            byte[] tempBuff = new byte[bytesRead];
            System.arraycopy(inBuff, 0, tempBuff, 0, bytesRead);
            outBuff = decoder.decode(tempBuff);
        }
        else
        {
            out.flush();
            out.close();
            in.close();
            return;
        }
        out.write(outBuff);
    }
}

特别感谢 @Jon Skeet 和 @Tagir Valeev。他们为我们提供了宝贵的技术支持。
4个回答

5
我强烈怀疑问题在于您忽略了从InputStream.read返回的值,除了检查是否到达流的结尾外。因此代码应该为:
while (in.read(inBuff) > 0) {
    // This always decodes the *complete* buffer
    outBuff = decoder.decode(inBuff);
    out.write(outBuff);
}

应该是

int bytesRead;
while ((bytesRead = in.read(inBuff)) > 0) {
    outBuff = decoder.decode(inBuff, 0, bytesRead);
    out.write(outBuff);
}

我不会指望这比使用 wrap 更快。


1
尝试使用decode.wrap(new BufferedInputStream(new FileInputStream(inputFileName)))。通过缓冲,它应该至少与您手动创建的版本一样快。
至于为什么您的代码不起作用:那是因为最后一个块很可能比1024个字节短,但您尝试解码整个byte []数组。有关详细信息,请参见@JonSkeet的答案。

0

好的,我把

"final int BUFF_SIZE = 1024;"

改成了

"final int BUFF_SIZE = 1024 * 3;"

它起作用了!

所以,我猜可能是填充出了问题...我的意思是,在编码文件时,(因为1024%3 = 1)必须进行填充。这些可能会在解码时引发问题...


2
这可能会导致编码问题(因为base64将每3个字节编码为4个),但不应该导致解码问题(在解码时,每4个字节都会转换为3个)。现在你的问题可能只是更隐蔽了(例如,它可能不报告异常,但会产生静默的错误结果)。 - Tagir Valeev

0
  • 你应该记录你已经读取的字节数,此外,
  • 你应该确保你的缓冲区大小是3的倍数,因为在Base64中,每3个字节有四个输出(64是2^6,而3*8等于4*6),这样做可以避免填充问题。(通过这种方式,你的输出将不会有错误的结尾“=”)

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