谈谈 Base64 编码

Base64 编码是日常的编程中经常用到的编码方案之一,尤其是在传输信息的时候。有些初学者搞不清楚它和普通字符串的区别,也有些初学者把它和文本加密混淆。这里对 Base64 编码做一个详细的介绍。

可打印字符和控制字符

介绍 Base64 编码之前,先介绍下什么是可打印字符。可打印字符(printable character)也称为图形字符(graphic character),指的是在 ASCII 以及类似的标准中,可以打印或展示出来给人读的字符。标准的 ASCII 码有7位用来编码,可表示128个字符,其中只有序号32-126的是可打印字符,比如 a-zA-Z0-9 和一些标点符号等。其他字符称为控制字符(control characters),它们的作用是不是用来展示信息,而是用来控制设备的。

Base64 是一组编码方案

Base64 是一组相似的从二进制到文本的编码方案,它使用一个基于64个可打印字符的表示方式,将二进制数据表示成 ASCII 字符串(即用 ASCII 码中的64个可打印字符组成的字符串)的形式。每个 Base64 的字符表示一个6位的二进制(2^6=64),由于平时计算机里的储存的数据都是按一个字节8位来表示的,所以4个 Base64 的字符恰好能表示3个字节的二进制数据,这样一来,像图片等非文本文件的数据就可以用文本来表示了。

为什么要有 Base64 编码方案

首先回答的是,为什么需要一个从二进制到文本的编码方案?这是因为有些协议(比如 HTTP 和邮件传输协议)是用纯文本而不是二进制数据进行数据传输的,如果内容本身就是文本没有问题,如果内容是图片、音频或视频等二进制文件,就需要先转码再传输了。

那么,就算要将二进制数据编码成文本,既然 Base64 将每6位二进制数据转换成 ASCII 码中的64个可打印字符之一,那为什么不直接用 ASCII 码来编码二进制数据呢?这是因为标准 ASCII 使用的位数是7位,并不是恰好8位的,因此如果直接编码,值在128到255之间的二进制值在标准 ASCII 码里没有对应的字符。同时,由于 ASCII 码中有33个是控制字符,而早期的网络协议中是不能传输这些控制字符的。

因此,就需要一个可以将二进制数据转换成可打印字符的专门的编码方式,Base64 编码最早就是起源于 MIME 的内容传输编码方法

Base64 介绍

上面已经介绍了 Base64 编码是什么以及为什么会有 Base64 编码,下面就来介绍下一些细节部分。首先先看 MIME 版本的 Base64 编码,它的二进制值和字符的对应关系如下:

可以看到前62个字符就是大小写字母和数字,剩下两个是 +/,这两个符号也是不同的 Base64 编码方案区别所在,比如用于 URL 编码的 Base64 方案 就是 -_,这是因为标准的 Base64 并不适合直接放在 URL 里传输,因为 URL 编码器会把标准 Base64 中的 /+ 字符变为形如 %XX 的形式,而这些 % 号在存入数据库时还需要再进行转换,因为 ANSI SQL 中已将 % 号用作通配符。

举个维基百科上的例子,Man 这个单词,M、a、n 对应的 ASCII 码分别是77、97和110,而这三个数字在计算机中存储的二进制是 01001101、01100001 和 01101110。接下来把三个字节共24位分成4组,每组6位,即010011、010110、000101和101110,并转换成10进制就是19、22、5 和 46,最后再去上面的编码表中找到对应的字符 T、W、F 和 u。那么 Man 的 Base64 编码后的结果就是 TWFu。

这是一个恰好可以按6位分组的例子,那么不足的情况呢?不足的情况有两种,一种是多一个字节,一种是多两个字节(多三个字节就被整除啦)。这种情况首先在后面补足0到24位,接下来,6个全时0的字节为1个 =,这样多一个字节的就补充量个 =,多两个字节的就补充一个 =。可以看下图的例子,很清晰:

理论上,解码的时候不需要 padding character(即这里的 =),因为可以算出来丢失的字节数。按每四个字符对应三个字节来解码后,会剩余两到三个字符(一个字符不可能,因为一个字符才6位,当时编码时8位字节取前6位后肯定有剩余),看情况补0就好。所以有些 Base64 的实现中是强制加上 padding characher 有些不是。

扫描二维码关注公众号,回复: 5799919 查看本文章

如上面所述,Base64 编码的实现方式的主要区别就在于最后两个字符的选择和时候加 padding character,具体的实现方式可以参考这里。这里说下 Base64 在 URL 上的编码实现,因为这在日常工作中很常见。由于 URL 编码机制里会对一些特殊字符做替换,比如 + 替换成 %2B/ 替换成 %2F,以及 = 替换成 %3D,所以在 URL 的处理上最后两个字符是 -_,而 = 是否换成其他符号还是省略就看具体的类库了,如果用来分隔两个 Base64 字符串就留着并用其他字符替代比如 .,如果不用就可以省略。

Data URI 中 Base64 的应用

一般 HTML 文档中,<img /> 标签都是引用的外部文件,比如 <img src='http://imgcdn.domain.com/hello.jpg' /> 这样的形式。这样是符合 HTML 标准的做法,但是在有些场景下,如果能够把图片直接嵌在文档中就更好了,比如一个页面有大量的很小的图片,如果都在加载的时候去服务器取的话加载速度会编码。这时候就可以用 Data URI 的方案了,它允许将图片内嵌在文档中,使用的正是 Base64 编码可以将二进制文件转换成文本的功能。

Data URI 不光可以用于图片,还能用在其他文件标签上,格式的标准方式是:data:[<media type>][;base64],<data>。举个例子:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />

此时,大部分浏览器都会将 Base64 编码的文本转换成图片渲染在 <img /> 标签的位置。

用 Data URI 来存储图片的场景除了上面说的图片很小的之外,还有个很重要的就是 HTML5 页面用在手机拍照上传的时候,此时手机拍的照片不需要存在别的地方等待上传,而是直接编码成 Base64 的格式存在标签上。

当然 Data URI 形式也有劣势,除了编码后体积大外,还有个重要的缺陷就是浏览器不能缓存,所以如果被访问的比较频繁的页面上图片还是用地址引用的形式比较好。

Base64 编码不能用来加密

初学者经常犯的一个错误就是认为 Base64 可以用来加密,这是个错误的观点。Base64 没有密钥,任何一个 Base64 编码后的字符串都可以轻松还原成初始文本。如果要可逆的加密,还是要去使用 DES、AES 以及 RSA 等算法,如果要不可逆的摘要,就去使用 MD5 和 SHA256 等算法。Base64 和 ASCII 码的区别在于,ASCII 码的目的是为了用二进制存储在表示文本,而 Base64 则是为了用文本来表示二进制存储,二者的目的是恰恰相反的。

参考

猜你喜欢

转载自www.cnblogs.com/LeewayHsu/p/10663304.html