Java Base64 解码结果意外不同

Java Base64 decode results differs unexpectedly

我有一个透明的 1x1 GIF 文件,其中包含以下数据:

$ xxd pixel.gif
00000000: 4749 4638 3961 0100 0100 f000 0000 0000  GIF89a..........
00000010: 0000 0021 f904 0100 0000 002c 0000 0000  ...!.......,....
00000020: 0100 0100 0002 0244 0100 3b              .......D..;

该文件的Base64编码数据如下:

$ openssl base64 -in pixel.gif
R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==

如果我解码这个字符串,我得到以下正确输出:

$ echo 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' | openssl base64 -d | xxd
00000000: 4749 4638 3961 0100 0100 f000 0000 0000  GIF89a..........
00000010: 0000 0021 f904 0100 0000 002c 0000 0000  ...!.......,....
00000020: 0100 0100 0002 0244 0100 3b

在 Java 中尝试解码此字符串时,我得到了意想不到的结果。考虑这个例子 Java 程序:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

import java.nio.charset.StandardCharsets;

import java.util.Base64;

public class Decode {
    public static void main(String[] args) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
            String line = reader.readLine();

            //System.out.println(line.getBytes());
            byte[] data = Base64.getDecoder().decode(line.getBytes());
            System.out.print(new String(data, 0, data.length, StandardCharsets.UTF_8));
        } catch (IOException e) {
            System.out.println("IOException reading System.in");
        }
    }
}

当我将编码的字符串通过管道传输到这个程序时,我得到以下结果

$ echo 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' | java Decode | xxd
00000000: 4749 4638 3961 0100 0100 efbf bd00 0000  GIF89a..........
00000010: 0000 0000 0021 efbf bd04 0100 0000 002c  .....!.........,
00000020: 0000 0000 0100 0100 0002 0244 0100 3b    ...........D..

我可以在第 11 个字节看到 0xf0 的预期输出更改为 0xef。整个二进制字符串现在是 47 个字节长,而不是 43 个字节长。为什么 Java 会发生这种情况?

您不能将任意二进制数据转换为 UTF-8 字符串。 UTF-8 是一种遵循一定规则的 unicode 编码(例如,所有多字节序列必须以 11 或 10 作为高位开始,多字节序列的第一个字节告诉解码器此多字节序列中包含多少字节)

你真正想要的是直接写字节数组,而不是先把它转换成字符串:

byte[] data = Base64.getDecoder().decode(line.getBytes());
System.out.write(data);