在 Java8中 Base64编码已经成为Java类库的标准,且内置了Base64编码的编码器和解码器。
一、什么是Base64? -- 来自百度百科Base64是网络上最常见的用于传输 8Bit字节码的编码方式之一,
Base64就是一种基于64个可打印字符来表示二进制数据的方法。
Base64一般用于在 HTTP协议下传输二进制数据,由于 HTTP协议是文本协议,所以在 HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。
1、什么是“可打印字符”呢?为什么要用它来传输 8Bit字节码呢?
在ASCII码中规定,0~31、127这33个字符属于控制字符,32~126这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?
其中一种方式就是使用Base64。
2、Base64的编码原理
Base64的原理比较简单,每当我们使用Base64时都会先定义一个类似这样的数组:
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
Base64采用了 "A-Z、a-z、0-9、+、/" 64个可打印字符,这是标准的Base64协议规定。在日常使用中我们还会看到 “=”或“==” 号出现在Base64的编码结果中,“=”在此是作为填充字符出现,后面会讲到。
Base64就是使用这64个可打印字符来表示二进制数据的方法。Base64的索引与对应字符的关系如下表所示:
索引
对应字符
索引
对应字符
索引
对应字符
索引
对应字符
0
A
17
R
34
i
51
z
1
B
18
S
35
j
52
0
2
C
19
T
36
k
53
1
3
D
20
U
37
l
54
2
4
E
21
V
38
m
55
3
5
F
22
W
39
n
56
4
6
G
23
X
40
o
57
5
7
H
24
Y
41
p
58
6
8
I
25
Z
42
q
59
7
9
J
26
a
43
r
60
8
10
K
27
b
44
s
61
9
11
L
28
c
45
t
62
+
12
M
29
d
46
u
63
/
13
N
30
e
47
v
14
O
31
f
48
w
15
P
32
g
49
x
16
Q
33
h
50
y
3、具体转换步骤
第一步,将待转换的字符串每三个字节分为一组,每个字节占 8bit,那么共有24个二进制位。
第二步,将上面的24个二进制位每6个一组,共分为4组。
第三步,在每组前面添加两个0,每组由6个变为8个二进制位,总共32个二进制位,即四个字节。
第四步,根据Base64的索引与对应字符的关系获得对应的字符值。
从上面的步骤我们发现:
Base64字符表中的字符原本用6个bit就可以表示,现在前面添加2个0,变为8个bit,会造成一定的浪费。因此,Base64编码之后的文本,要比原文多大约三分之一。
为什么使用3个字节一组呢?
因为6和8的最小公倍数为24,三个字节正好24个二进制位,每6个bit位一组,恰好能够分为4组。
4、示例说明
1)以下图的表格为示例,我们具体分析一下整个过程。
第一步:字符串“Man”为3个字节,“M”、“a”、"n"对应的ASCII码值分别为77,97,110,对应的二进制值是01001101、01100001、01101110。如图第二三行所示,由此组成一个24位的二进制字符串。
第二步:如图红色框,将24位每6位二进制位一组分成四组。
第三步:在上面每一组前面补两个0,扩展成32个二进制位,此时变为四个字节:00010011、00010110、00000101、00101110。分别对应的值(Base64编码索引)为:19、22、5、46。
第四步:用上面的值在Base64编码表中进行查找,分别对应:T、W、F、u。因此“Man”Base64编码之后就变为:TWFu。
2)位数不足情况
上面是按照三个字节来举例说明的,如果字节数不足三个,那么该如何处理?
两个字节:两个字节共16个二进制位,依旧按照规则进行分组。此时总共16个二进制位,每6个一组,则第三组缺少2位,用0补齐,得到三个Base64编码,第四组完全没有数据则用“=”补上。因此,上图中“BC”转换之后为“QKM=”;
一个字节:一个字节共8个二进制位,依旧按照规则进行分组。此时共8个二进制位,每6个一组,则第二组缺少4位,用0补齐,得到两个Base64编码,而后面两组没有对应数据,都用“=”补上。因此,上图中“A”转换之后为“QQ==”;
3)注意事项
大多数编码都是由字符串转化成二进制的过程,而Base64的编码则是从二进制转换为字符串。与常规恰恰相反,
Base64编码主要用在传输、存储、表示二进制领域,不能算得上加密,只是无法直接看到明文。也可以通过打乱Base64编码来进行加密。
中文有多种编码(比如:utf-8、gb2312、gbk等),不同编码对应Base64编码结果都不一样。
延伸:
上面我们已经看到了Base64就是用6位(2的6次幂就是64)表示字符,因此成为Base64。同理,Base32就是用5位,Base16就是用4位。大家可以按照上面的步骤进行演化一下。
二、Java8中实现Base64在 Java8中 Base64编码已经成为 Java类库的标准,且内置了 Base64编码的编码器和解码器。
新的 Base64API也支持 URL和 MINE的编码解码。我们直接调用即可。
java.util.Base64 类仅由用于获得 Base64编码方案的编码器和解码器的静态方法组成。
-
-
static Base64.Decoder
getDecoder()
返回Base64.Decoder解码使用Basic型base64编码方案。
static Base64.Encoder
getEncoder()
返回一个Base64.Encoder编码使用Basic型base64编码方案。
static Base64.Decoder
getMimeDecoder()
返回一个Base64.Decoder解码使用MIME型BASE64解码方案。
static Base64.Encoder
getMimeEncoder()
返回一个Base64.Encoder编码使用MIME型base64编码方案。
static Base64.Encoder
getMimeEncoder(int lineLength, byte[] lineSeparator)
返回一个Base64.Encoder ,它使用具有指定行长度和行分隔符的MIME类型base64编码方案进行编码。
static Base64.Decoder
getUrlDecoder()
返回Base64.Decoder解码使用URL and Filename safe型base64编码方案。
static Base64.Encoder
getUrlEncoder()
返回一个Base64.Encoder编码使用URL and Filename safe型base64编码方案。
-
java.util.Base64.Encoder 类使用 RFC 4648和 RFC 2045中规定的 Base64编码方案来实现用于编码字节数据的编码器。
-
-
byte[]
encode(byte[] src)
使用Base64编码方案将指定字节数组中的所有字节编码为新分配的字节数组。
int
encode(byte[] src, byte[] dst)
使用Base64编码方案对来自指定字节数组的所有字节进行编码,将生成的字节写入给定的输出字节数组,从偏移0开始。
ByteBuffer
encode(ByteBuffer buffer)
使用Base64编码方案将所有剩余字节从指定的字节缓冲区编码到新分配的ByteBuffer中。
String
encodeToString(byte[] src)
使用Base64编码方案将指定的字节数组编码为字符串。
Base64.Encoder
withoutPadding()
返回一个编码器实例,编码器等效于此编码器实例,但不会在编码字节数据的末尾添加任何填充字符。
OutputStream
wrap(OutputStream os)
使用Base64编码方案包装用于编码字节数据的输出流。
-
java.util.Base64.Decoder 该类使用 RFC 4648和RFC 2045中规定的 Base64编码方案来实现用于解码字节数据的解码器。
-
-
byte[]
decode(byte[] src)
使用Base64编码方案从输入字节数组中解码所有字节,将结果写入新分配的输出字节数组。
int
decode(byte[] src, byte[] dst)
使用Base64编码方案从输入字节数组中解码所有字节,将结果写入给定的输出字节数组,从偏移0开始。
ByteBuffer
decode(ByteBuffer buffer)
使用Base64编码方案从输入字节缓冲区中解码所有字节,将结果写入新分配的ByteBuffer。
byte[]
decode(String src)
使用Base64编码方案将Base64编码的字符串解码为新分配的字节数组。
InputStream
wrap(InputStream is)
返回一个输入流,用于解码Base64编码字节流。
-
加密字符串测试 demo
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Demo {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "hello Base64 啊啊\n" +
"ADCDEFG";
//编码加密
String encodeStr = Base64.getEncoder().encodeToString(str.getBytes("UTF-8"));
System.out.println("加密后的字符串为:" + encodeStr);
//解码解密
String decoderStr = new String(Base64.getDecoder().decode(encodeStr), StandardCharsets.UTF_8); //
// 推荐使用StandardCharsets类指定
System.out.println("解密后的字符串为" + decoderStr);
}
}
JDK8 的 Base64类是基于 RFC 4648和 RFC 2045实现的。
根据上文列出的协议内容可以确定,该类的编码结果不会包含换行符,而且在类的注释中明确说明了不会添加换行符。解码会正常还原。
结论:针对比较长的原文进行 base64编码:
- jdk7的编码结果包含换行;
- jdk8的编码结果不包含换行;
- jdk8无法解码包含换行的编码结果;
- jdk8的编码结果使用jdk7进行解码,没有任何问题,不再演示
1、Apache Common
Apache Common中的 org.apache.commons.codec.binary.Base64类是基于 RFC 2045实现的,根据类注释可以了解到此实现解码时忽略了所有不在 the base64 alphabet范围内的字符,所以该实现可以处理包含换行符的 base64编码结果。
同时该类的编码方法提供了参数,用于指定编码结果长度在超过76个字符的时候是否添加换行,默认不换行。
2、Spring Core
Spring Core提供了 org.springframework.util.Base64Utils类,该类只是一个工具类,并没有实现任何协议。
优先使用 java8中的 java.util.Base64类进行编码和解码;
如果 java.util.Base64不存在,则会使用 org.apache.commons.codec.binary.Base64;
如果都不存在,则会报错
3、协议简述(RFC 1521、RFC 2045、和RFC 4648)可查看参考文章
参考文章:这两篇文章对理解 Base64 很有帮助,感谢前辈
一篇文章彻底弄懂Base64编码原理
升级JDK8的坑----base64
站在前辈的肩膀上,每天进步一点点 ends~