Java 8 java.util.Base64 是 sun.misc.BASE64 的替代品吗?
Is Java 8 java.util.Base64 a drop-in replacement for sun.misc.BASE64?
问题
Java8java.util.Base64
MIME 编码器和解码器是否为不受支持的 , 内部 Java API sun.misc.BASE64Encoder
和 sun.misc.BASE64Decoder
?
编辑(澄清):通过直接替换
我的意思是我可以将使用 sun.misc.BASE64Encoder
和 sun.misc.BASE64Decoder
的旧代码透明地切换到 Java 8 MIME Base64 Encoder/Decoder 以用于任何现有的其他客户端代码。
到目前为止我的想法和原因
根据我的调查和快速测试(见下面的代码)它应该是一个替代品 因为
sun.misc.BASE64Encoder
基于其 JavaDoc 是 一个 BASE64 字符编码器,如 RFC1521 中所指定。此 RFC 是 MIME 规范的一部分...
java.util.Base64
基于其 JavaDoc 使用 Table 1 of RFC 2045[=70] 中指定的“The Base64 Alphabet” =] 用于编码和解码操作...MIME 下的
假设 RFC 1521 和 2045 没有重大变化(我找不到任何变化)并且基于我使用 Java 8 Base64 MIME Encoder/Decoder 的快速测试应该没问题。
我在找什么
- 权威来源确认或反驳“直接替换”点或
- 一个反例,显示 java.util.Base64 与 sun.misc.BASE64Encoder OpenJDK Java 8 implementation (8u40-b25) (BASE64Decoder) OR
具有不同行为的情况
- 随便你怎么想就回答上面的问题绝对
供参考
我的测试代码
public class Base64EncodingDecodingRoundTripTest {
public static void main(String[] args) throws IOException {
String test1 = " ~!@#$%^& *()_+=`| }{[]\;: \"?><,./ ";
String test2 = test1 + test1;
encodeDecode(test1);
encodeDecode(test2);
}
static void encodeDecode(final String testInputString) throws IOException {
sun.misc.BASE64Encoder unsupportedEncoder = new sun.misc.BASE64Encoder();
sun.misc.BASE64Decoder unsupportedDecoder = new sun.misc.BASE64Decoder();
Base64.Encoder mimeEncoder = java.util.Base64.getMimeEncoder();
Base64.Decoder mimeDecoder = java.util.Base64.getMimeDecoder();
String sunEncoded = unsupportedEncoder.encode(testInputString.getBytes());
System.out.println("sun.misc encoded: " + sunEncoded);
String mimeEncoded = mimeEncoder.encodeToString(testInputString.getBytes());
System.out.println("Java 8 Base64 MIME encoded: " + mimeEncoded);
byte[] mimeDecoded = mimeDecoder.decode(sunEncoded);
String mimeDecodedString = new String(mimeDecoded, Charset.forName("UTF-8"));
byte[] sunDecoded = unsupportedDecoder.decodeBuffer(mimeEncoded); // throws IOException
String sunDecodedString = new String(sunDecoded, Charset.forName("UTF-8"));
System.out.println(String.format("sun.misc decoded: %s | Java 8 Base64 decoded: %s", sunDecodedString, mimeDecodedString));
System.out.println("Decoded results are both equal: " + Objects.equals(sunDecodedString, mimeDecodedString));
System.out.println("Mime decoded result is equal to test input string: " + Objects.equals(testInputString, mimeDecodedString));
System.out.println("\n");
}
}
rfc1521 和 rfc2045 之间的 base64 规范没有变化。
所有 base64 实现都可以被认为是彼此的直接替换,base64 实现之间的唯一区别是:
- 使用的字母表。
- API 提供(例如,有些可能只对完整的输入缓冲区采取行动,而其他可能是有限状态机,允许您继续通过它们推送输入块,直到完成) .
MIME base64 字母表在 RFC 版本之间保持不变(它 有 或旧软件会损坏)并且是:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/
如 Wikipedia 所述,只有最后 2 个字符在 base64 实现之间可能会发生变化。
作为 更改最后 2 个字符的 base64 实现示例,IMAP MUTF-7 规范使用以下 base64 字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+,
更改的原因是 /
字符经常用作路径分隔符,并且由于 MUTF-7 编码用于将非 ASCII 目录路径扁平化为 ASCII,因此 /
编码段中需要避免字符。
假设两个编码器都没有错误,那么 RFC 要求对每个 0 字节、1 字节、2 字节和 3 字节序列进行不同的编码。较长的序列根据需要被分解成尽可能多的 3 字节序列,然后是最终序列。因此,如果这两个实现正确处理了所有 16,843,009 (1+256+65536+16777216) 个可能的序列,那么这两个实现也是相同的。
这些测试只需要几分钟 运行。通过稍微更改您的测试代码,我已经做到了,并且我的 Java 8 安装通过了所有测试。因此,public 实现可用于安全地替换 sun.misc 实现。
这是我的测试代码:
import java.util.Base64;
import java.util.Arrays;
import java.io.IOException;
public class Base64EncodingDecodingRoundTripTest {
public static void main(String[] args) throws IOException {
System.out.println("Testing zero byte encoding");
encodeDecode(new byte[0]);
System.out.println("Testing single byte encodings");
byte[] test = new byte[1];
for(int i=0;i<256;i++) {
test[0] = (byte) i;
encodeDecode(test);
}
System.out.println("Testing double byte encodings");
test = new byte[2];
for(int i=0;i<65536;i++) {
test[0] = (byte) i;
test[1] = (byte) (i >>> 8);
encodeDecode(test);
}
System.out.println("Testing triple byte encodings");
test = new byte[3];
for(int i=0;i<16777216;i++) {
test[0] = (byte) i;
test[1] = (byte) (i >>> 8);
test[2] = (byte) (i >>> 16);
encodeDecode(test);
}
System.out.println("All tests passed");
}
static void encodeDecode(final byte[] testInput) throws IOException {
sun.misc.BASE64Encoder unsupportedEncoder = new sun.misc.BASE64Encoder();
sun.misc.BASE64Decoder unsupportedDecoder = new sun.misc.BASE64Decoder();
Base64.Encoder mimeEncoder = java.util.Base64.getMimeEncoder();
Base64.Decoder mimeDecoder = java.util.Base64.getMimeDecoder();
String sunEncoded = unsupportedEncoder.encode(testInput);
String mimeEncoded = mimeEncoder.encodeToString(testInput);
// check encodings equal
if( ! sunEncoded.equals(mimeEncoded) ) {
throw new IOException("Input "+Arrays.toString(testInput)+" produced different encodings (sun=\""+sunEncoded+"\", mime=\""+mimeEncoded+"\")");
}
// Check cross decodes are equal. Note encoded forms are identical
byte[] mimeDecoded = mimeDecoder.decode(sunEncoded);
byte[] sunDecoded = unsupportedDecoder.decodeBuffer(mimeEncoded); // throws IOException
if(! Arrays.equals(mimeDecoded,sunDecoded) ) {
throw new IOException("Input "+Arrays.toString(testInput)+" was encoded as \""+sunEncoded+"\", but decoded as sun="+Arrays.toString(sunDecoded)+" and mime="+Arrays.toString(mimeDecoded));
}
}
}
这是一个小测试程序,它说明了编码字符串的差异:
byte[] bytes = new byte[57];
String enc1 = new sun.misc.BASE64Encoder().encode(bytes);
String enc2 = new String(java.util.Base64.getMimeEncoder().encode(bytes),
StandardCharsets.UTF_8);
System.out.println("enc1 = <" + enc1 + ">");
System.out.println("enc2 = <" + enc2 + ">");
System.out.println(enc1.equals(enc2));
它的输出是:
enc1 = <AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>
enc2 = <AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>
false
请注意,sun.misc.BASE64Encoder
的编码输出在末尾有一个换行符。它不会总是追加换行符,但如果编码字符串的最后一行刚好有 76 个字符,它就会这样做。 (java.util.Base64
的作者认为这是 sun.misc.BASE64Encoder
实现中的一个小错误——参见 review thread)。
这看似微不足道,但如果您的程序依赖于此特定行为,则切换编码器可能会导致格式错误的输出。因此,我得出结论 java.util.Base64
是 而不是 sun.misc.BASE64Encoder
.
的替代品
当然,java.util.Base64
的 意图 是功能等效、符合 RFC、高性能、完全支持和指定的替代品,旨在支持从 sun.misc.BASE64Encoder
迁移代码。不过,您在迁移时需要注意一些像这样的边缘情况。
我遇到了同样的问题,当我从 sun
移动到 java.util.base64
时,但是 org.apache.commons.codec.binary.Base64
解决了我的问题
问题
Java8java.util.Base64
MIME 编码器和解码器是否为不受支持的 , 内部 Java API sun.misc.BASE64Encoder
和 sun.misc.BASE64Decoder
?
编辑(澄清):通过直接替换
我的意思是我可以将使用 sun.misc.BASE64Encoder
和 sun.misc.BASE64Decoder
的旧代码透明地切换到 Java 8 MIME Base64 Encoder/Decoder 以用于任何现有的其他客户端代码。
到目前为止我的想法和原因
根据我的调查和快速测试(见下面的代码)它应该是一个替代品 因为
sun.misc.BASE64Encoder
基于其 JavaDoc 是 一个 BASE64 字符编码器,如 RFC1521 中所指定。此 RFC 是 MIME 规范的一部分...java.util.Base64
基于其 JavaDoc 使用 Table 1 of RFC 2045[=70] 中指定的“The Base64 Alphabet” =] 用于编码和解码操作...MIME 下的
假设 RFC 1521 和 2045 没有重大变化(我找不到任何变化)并且基于我使用 Java 8 Base64 MIME Encoder/Decoder 的快速测试应该没问题。
我在找什么
- 权威来源确认或反驳“直接替换”点或
- 一个反例,显示 java.util.Base64 与 sun.misc.BASE64Encoder OpenJDK Java 8 implementation (8u40-b25) (BASE64Decoder) OR 具有不同行为的情况
- 随便你怎么想就回答上面的问题绝对
供参考
我的测试代码
public class Base64EncodingDecodingRoundTripTest {
public static void main(String[] args) throws IOException {
String test1 = " ~!@#$%^& *()_+=`| }{[]\;: \"?><,./ ";
String test2 = test1 + test1;
encodeDecode(test1);
encodeDecode(test2);
}
static void encodeDecode(final String testInputString) throws IOException {
sun.misc.BASE64Encoder unsupportedEncoder = new sun.misc.BASE64Encoder();
sun.misc.BASE64Decoder unsupportedDecoder = new sun.misc.BASE64Decoder();
Base64.Encoder mimeEncoder = java.util.Base64.getMimeEncoder();
Base64.Decoder mimeDecoder = java.util.Base64.getMimeDecoder();
String sunEncoded = unsupportedEncoder.encode(testInputString.getBytes());
System.out.println("sun.misc encoded: " + sunEncoded);
String mimeEncoded = mimeEncoder.encodeToString(testInputString.getBytes());
System.out.println("Java 8 Base64 MIME encoded: " + mimeEncoded);
byte[] mimeDecoded = mimeDecoder.decode(sunEncoded);
String mimeDecodedString = new String(mimeDecoded, Charset.forName("UTF-8"));
byte[] sunDecoded = unsupportedDecoder.decodeBuffer(mimeEncoded); // throws IOException
String sunDecodedString = new String(sunDecoded, Charset.forName("UTF-8"));
System.out.println(String.format("sun.misc decoded: %s | Java 8 Base64 decoded: %s", sunDecodedString, mimeDecodedString));
System.out.println("Decoded results are both equal: " + Objects.equals(sunDecodedString, mimeDecodedString));
System.out.println("Mime decoded result is equal to test input string: " + Objects.equals(testInputString, mimeDecodedString));
System.out.println("\n");
}
}
rfc1521 和 rfc2045 之间的 base64 规范没有变化。
所有 base64 实现都可以被认为是彼此的直接替换,base64 实现之间的唯一区别是:
- 使用的字母表。
- API 提供(例如,有些可能只对完整的输入缓冲区采取行动,而其他可能是有限状态机,允许您继续通过它们推送输入块,直到完成) .
MIME base64 字母表在 RFC 版本之间保持不变(它 有 或旧软件会损坏)并且是:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/
如 Wikipedia 所述,只有最后 2 个字符在 base64 实现之间可能会发生变化。
作为 更改最后 2 个字符的 base64 实现示例,IMAP MUTF-7 规范使用以下 base64 字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+,
更改的原因是 /
字符经常用作路径分隔符,并且由于 MUTF-7 编码用于将非 ASCII 目录路径扁平化为 ASCII,因此 /
编码段中需要避免字符。
假设两个编码器都没有错误,那么 RFC 要求对每个 0 字节、1 字节、2 字节和 3 字节序列进行不同的编码。较长的序列根据需要被分解成尽可能多的 3 字节序列,然后是最终序列。因此,如果这两个实现正确处理了所有 16,843,009 (1+256+65536+16777216) 个可能的序列,那么这两个实现也是相同的。
这些测试只需要几分钟 运行。通过稍微更改您的测试代码,我已经做到了,并且我的 Java 8 安装通过了所有测试。因此,public 实现可用于安全地替换 sun.misc 实现。
这是我的测试代码:
import java.util.Base64;
import java.util.Arrays;
import java.io.IOException;
public class Base64EncodingDecodingRoundTripTest {
public static void main(String[] args) throws IOException {
System.out.println("Testing zero byte encoding");
encodeDecode(new byte[0]);
System.out.println("Testing single byte encodings");
byte[] test = new byte[1];
for(int i=0;i<256;i++) {
test[0] = (byte) i;
encodeDecode(test);
}
System.out.println("Testing double byte encodings");
test = new byte[2];
for(int i=0;i<65536;i++) {
test[0] = (byte) i;
test[1] = (byte) (i >>> 8);
encodeDecode(test);
}
System.out.println("Testing triple byte encodings");
test = new byte[3];
for(int i=0;i<16777216;i++) {
test[0] = (byte) i;
test[1] = (byte) (i >>> 8);
test[2] = (byte) (i >>> 16);
encodeDecode(test);
}
System.out.println("All tests passed");
}
static void encodeDecode(final byte[] testInput) throws IOException {
sun.misc.BASE64Encoder unsupportedEncoder = new sun.misc.BASE64Encoder();
sun.misc.BASE64Decoder unsupportedDecoder = new sun.misc.BASE64Decoder();
Base64.Encoder mimeEncoder = java.util.Base64.getMimeEncoder();
Base64.Decoder mimeDecoder = java.util.Base64.getMimeDecoder();
String sunEncoded = unsupportedEncoder.encode(testInput);
String mimeEncoded = mimeEncoder.encodeToString(testInput);
// check encodings equal
if( ! sunEncoded.equals(mimeEncoded) ) {
throw new IOException("Input "+Arrays.toString(testInput)+" produced different encodings (sun=\""+sunEncoded+"\", mime=\""+mimeEncoded+"\")");
}
// Check cross decodes are equal. Note encoded forms are identical
byte[] mimeDecoded = mimeDecoder.decode(sunEncoded);
byte[] sunDecoded = unsupportedDecoder.decodeBuffer(mimeEncoded); // throws IOException
if(! Arrays.equals(mimeDecoded,sunDecoded) ) {
throw new IOException("Input "+Arrays.toString(testInput)+" was encoded as \""+sunEncoded+"\", but decoded as sun="+Arrays.toString(sunDecoded)+" and mime="+Arrays.toString(mimeDecoded));
}
}
}
这是一个小测试程序,它说明了编码字符串的差异:
byte[] bytes = new byte[57];
String enc1 = new sun.misc.BASE64Encoder().encode(bytes);
String enc2 = new String(java.util.Base64.getMimeEncoder().encode(bytes),
StandardCharsets.UTF_8);
System.out.println("enc1 = <" + enc1 + ">");
System.out.println("enc2 = <" + enc2 + ">");
System.out.println(enc1.equals(enc2));
它的输出是:
enc1 = <AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>
enc2 = <AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>
false
请注意,sun.misc.BASE64Encoder
的编码输出在末尾有一个换行符。它不会总是追加换行符,但如果编码字符串的最后一行刚好有 76 个字符,它就会这样做。 (java.util.Base64
的作者认为这是 sun.misc.BASE64Encoder
实现中的一个小错误——参见 review thread)。
这看似微不足道,但如果您的程序依赖于此特定行为,则切换编码器可能会导致格式错误的输出。因此,我得出结论 java.util.Base64
是 而不是 sun.misc.BASE64Encoder
.
当然,java.util.Base64
的 意图 是功能等效、符合 RFC、高性能、完全支持和指定的替代品,旨在支持从 sun.misc.BASE64Encoder
迁移代码。不过,您在迁移时需要注意一些像这样的边缘情况。
我遇到了同样的问题,当我从 sun
移动到 java.util.base64
时,但是 org.apache.commons.codec.binary.Base64
解决了我的问题