Base64是什么?
Base64是一种二进制到文本的编码方式。如果要更具体一点的话,可以认为它是一种将 byte
数组编码为字符串的方法,而且编码出的字符串只包含ASCII基础字符。
例如字符串ShuSheng007
对应的Base64为U2h1U2hlbmcwMDc=
。其中那个=
比较特殊,是填充符,一会再说。
值得注意的是Base64不是加密算法,其仅仅是一种编码方式,算法也是公开的,所以不能依赖它进行加密。
为什么叫Base64?
因为它是基于(Base)64个字符的一种编码方式。使用其编码后的文本只包含64个ASCII码字符(偶尔加一个填充字符=
),如下所示:
Base64使用到的64个字符:
A-Z
26个a-z
26个0-9
10个+
1个/
1个
下图是Base64码表,可以看到从0到63的每个数字都对应一个上面的一个字符。
Base64解决什么问题?
Base64编码是从二进制值到某些特定字符的编码,这些特定字符一共64个,所以称作Base64。
为什么不直接传输二进制呢?比如图片,或者字符,既然实际传输时它们都是二进制字节流。而且即使Base64编码过的字符串最终也是二进制(通常是UTF-8编码,兼容ASCII编码)在网络上传输的,那么用4/3倍带宽传输数据的Base64究竟有什么意义?
真正的原因是二进制不兼容。某些二进制值,在一些硬件上,比如在不同的路由器,老电脑上,表示的意义不一样,做的处理也不一样。同样,一些老的软件,网络协议也有类似的问题。
在项目中,对报文进行压缩、加密后,最后一步一般是 base64 编码。因为 base64 编码的字符串更适合不同平台,不同语言的传输。
base64 编码的优点:
- 算法是编码,不是压缩,编码后只会增加字节数(一般是比之前的多1/3,比如之前是3, 编码后是4)
- 算法简单,基本不影响效率
- 算法可逆,解码很方便,不用于私密传输。
- 毕竟编码了,肉眼不能直接读出原始内容。
- 加密后的字符串只有【0-9a-zA-Z+/=】 不可打印字符(转译字符)也可以传输
Base64就是为了解决各系统以及传输协议中二进制不兼容的问题而生的
//二进制数据传输过程中,不可见字符或者无法由UTF-8解码的二进制数据(一个字符对应一个二进制编码,但是一个二进制不一定能对应上一个字符),数据可能丢失
public static void main(String[] args) {
//字节数组,经常用来表示二进制数据
byte[] bytes1 = new byte[]{31, -117, 8};
//直接用字符串来转换字节数组(默认UTF-8编码)
String str = new String(bytes1);
System.out.println(str);//
byte[] bytes2 = str.getBytes();
System.out.println(Arrays.toString(bytes2)); //结果[31, -17, -65, -67, 8] 和原来的bytes1:[31,-117,8]并不相同
//这是为什么呢?
//UTF-8编码是一种变长编码,一个字符对应一个二进制编码,但是一个二进制不一定能对应上一个字符
//如果采用 Ascii码(UTF-8兼容)中的数据,就可以完成映射,并且不丢失数据
byte[] bytes3 = new byte[]{49, 50, 51};
String str2 = new String(bytes3);
System.out.println(str2);
byte[] bytes4 = str2.getBytes();
System.out.println(Arrays.toString(bytes4));//结果:[49, 50, 51] 和 bytes3[49,50,51]就相同了
//其它的大多数编码方法,都是一个字符对应一个二进制编码,但是一个二进制编码不一定呢个对应上一个字符
//但是Base64可以,是如何解决的呢?
//Base64,采用6位一个单元,去Ascii中取出了64个可见字符来做映射(A-Z a-z 0-9 + /),
// 这样所有的二进制都能根据长度分割为每6位对应一个字符,不够6的倍数会用 0补全, 如果6位都是0,则映射为 = 号
//通过Base64编码后
String encode = Base64.encode(bytes1);
System.out.println(encode);//H4sI
byte[] decode = Base64.decode(encode);
System.out.println(Arrays.toString(decode));//结果:[31, -117, 8] 和原来的bytes1:[31,-117,8]相同了
}
编码Man
三个字符Man编码后为四个字符TWFu
由此看出原数据一个字节需要8位,base64编码需要6位,所以原数据字节数必须是8与6的公约数,也就是3的倍数
当然如果要需要编码的字节数不是3的倍数,就需要多出1或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行Base64的编码。在编码后的Base64文本后加上一个或两个 =
号,代表补足的字节数。也就是说,当最后剩余两个八位(待补足)字节(2个byte)时,最后一个6位的Base64字节块有四位(2*6-8=4)是0值,最后附加上两个等号;如果最后剩余一个八位(待补足)字节(1个byte)时,最后一个6位的base字节块有两位(3*6-2*8=2)是0值,最后附加一个等号。 参考下表:
所以Base64编码后的数据比原数据略长,为原来的4/3
.
Base64 DataURI格式
有时你会发现web页面传给你的base64字符串前面有类似下面的东东。
data:image/jpeg;base64, /9j/4AA...
这是DataURI,大部分浏览器支持直接打开这类二进制数据,但是我们要格外注意,如果你只是想要真实的Base64内容就需要取,
后边的内容
Base64变种
Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在Java持久化系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bit的UUID)编码为一个字符串,用作HTTP表单和HTTP GET URL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。
然而,标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的/
和+
字符变为形如%XX
的形式,而这些%
号在存入数据库时还需要再进行转换,因为ANSI SQL中已将%
号用作通配符。
为解决此问题,可采用一种用于URL的改进Base64编码,它不在末尾填充=
号,并将标准Base64中的+
和/
分别改成了-
和_
,这样就免去了在URL编解码和数据库存储时所要做的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。
另有一种用于正则表达式的改进Base64变种,它将+
和/
改成了!
和-
,因为+
,*
以及前面在IRCu中用到的[
和]
在正则表达式中都可能具有特殊含义。
此外还有一些变种,它们将+/
改为_-
或._
(用作编程语言中的标识符名称)或.-
(用于XML中的Nmtoken)甚至_:
(用于XML中的Name)。
用途
- 对于证书来说,尤其是根证书,一般是 base64 编码的,在网上被很多人下载
- 电子邮件的附件一般是 base64 编码,因为附件往往有不可见字符
- xml 中如果像嵌入另外一个 xml 文件,直接嵌入,往往 xml 标签就乱套了, 不容易解析,因此,需要把 xml 编译成字节数组的字符串,编译成可见字符。
- 网页中的一些小图片,可以直接以 base64 编码的方式嵌入,不用再链接请求消耗网络资源。
外一个 xml 文件,直接嵌入,往往 xml 标签就乱套了, 不容易解析,因此,需要把 xml 编译成字节数组的字符串,编译成可见字符。 - 网页中的一些小图片,可以直接以 base64 编码的方式嵌入,不用再链接请求消耗网络资源。
- 较老的纯文本协议 SMTP ,这些文本偶尔传输一个文件时,需要用 base64