Base64编码
# 22.Base64编码
URL编码是对字符进行编码,表示成%xx
的形式,而Base64编码是对二进制数据进行编码,表示成文本格式。
# 为什么需要Base64编码
Base64编码的目的是把二进制数据变成文本格式,这样在很多文本中就可以处理二进制数据。例如,电子邮件协议就是文本协议,如果要在电子邮件中添加一个二进制文件(比如图片),就可以用Base64编码,然后以文本的形式传送。
而且有时候一些路由器等硬件也不兼容二进制,或者一些网络协议不兼容,因此得转为字符形式。
还有的时候,使用文本形式能减少一次http请求,提高效率:打开google的首页,就能看到某些样式中的图片不是一个资源地址,而是base64编码的字符串。通过base64来传输图片,然后浏览器解码该base64,就能得到图片了。注意,并不是什么图片都适合用base64来处理,因为图片越大,转换的base64的字符串就越长,对带宽的要求更高了。
# Base64编码转换规则
Base64编码可以把任意长度的二进制数据变为纯文本,且只包含A
~Z
,a
~z
,0
~9
、+
、/
、=
这些字符。它的原理是把3字节的二进制数据按6bit一组,用4个int整数表示,然后把这个整数作为索引查表,得到对应的字符,最终得到编码后的字符串。
因为6位整数的范围总是0
~63
,所以,能用64个字符表示:字符A
~Z
对应索引0
~25
,字符a
~z
对应索引26
~51
,字符0
~9
对应索引52
~61
,最后两个索引62
、63
分别用字符+
和/
表示。
索引表如下(摘自RFC2045):
Table 1: The Base64 Alphabet
索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 |
---|---|---|---|---|---|---|---|
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个byte数据分别是e4、b8、ad,按6bit分组得到39、0b、22和2d(十六进制下):
┌───────────────┬───────────────┬───────────────┐
│ e4 │ b8 │ ad │
└───────────────┴───────────────┴───────────────┘
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│1│1│1│0│0│1│0│0│1│0│1│1│1│0│0│0│1│0│1│0│1│1│0│1│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
┌───────────┬───────────┬───────────┬───────────┐
│ 39 │ 0b │ 22 │ 2d │
└───────────┴───────────┴───────────┴───────────┘
2
3
4
5
6
7
8
9
根据查表可得最后的字符是5Lit
这四个字符。
需要注意的是,根据ASCII编码,4个字符占了 4*8=32个bit,比起原始的bit位数多了1/3,传输效率会降低。字符越少,编码的效率就会越低。
如果把Base64的64个字符编码表换成32个、48个或者58个,就可以使用Base32编码,Base48编码和Base58编码。
# Java 中的Base64编码
Java提供了不少类来对二进制数据进行Base64编码和解码。
例如对上述例子进行编码:
import java.util.Base64;
public class TestBase64{
public static void main(String[] args) {
byte[] input = new byte[]{ (byte)0xe4, (byte)0xb8, (byte)0xad};
String base64Encoded = Base64.getEncoder().encodeToString(input);
System.out.println(base64Encoded); //5Lit
}
}
2
3
4
5
6
7
8
9
解码:
byte[] output = Base64.getDecoder().decode("5Lit");
System.out.println(Arrays.toString(output)); //[-28, -72, -83]
2
以补码形式输出结果
# 如果不是3的倍数...
如果输入的byte[]
数组长度不是3的整数倍肿么办?这种情况下,需要对输入的末尾补一个或两个0x00
,编码后,在结尾加一个=
表示补充了1个0x00
,加两个=
表示补充了2个0x00
,解码的时候,去掉末尾补充的一个或两个0x00
即可。
实际上,因为编码后的长度加上=
总是4的倍数,所以即使不加=
也可以计算出原始输入的byte[]
。Base64编码的时候可以用withoutPadding()
去掉=
,解码出来的结果是一样的:
import java.util.Arrays;
import java.util.Base64;
public class TestBase642 {
public static void main(String[] args) {
byte[] input = new byte[] { (byte) 0xe4, (byte) 0xb8, (byte) 0xad, 0x21 };
String b64encoded = Base64.getEncoder().encodeToString(input);
String b64encoded2 = Base64.getEncoder().withoutPadding().encodeToString(input);
System.out.println(b64encoded);
System.out.println(b64encoded2);
byte[] output = Base64.getDecoder().decode(b64encoded2);
System.out.println(Arrays.toString(output));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
运行结果:
5LitIQ==
5LitIQ
[-28, -72, -83, 33]
2
3
因为标准的Base64编码会出现+
、/
和=
,所以不适合把Base64编码后的字符串放到URL中。一种针对URL的Base64编码可以在URL中使用的Base64编码,它仅仅是把+
变成-
,/
变成_
:
import java.util.Arrays;
import java.util.Base64;
public class TestBase643 {
public static void main(String[] args) {
byte[] input = new byte[] { 0x01, 0x02, 0x7f, 0x00 };
String b64encoded = Base64.getUrlEncoder().encodeToString(input);
System.out.println(b64encoded);
byte[] output = Base64.getUrlDecoder().decode(b64encoded);
System.out.println(Arrays.toString(output));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
运行结果:
AQJ_AA==
[1, 2, 127, 0]
2
# 参考
编码算法 - 廖雪峰的官方网站 (opens new window)