我有一个透明的 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);
关于Java Base64 解码结果意外不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59107247/