前端开发中的 Base64 编解码

1,231 阅读4分钟

Base64 编码是一种二进制到文本 (binary-to-text) 的编码规则,它使用了 64 个字符(通常是 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/)来编码一段二进制数据(可以理解为六十四进制)。由于 Base64 编码时用到的字符都是 ASCII 字符集里的可显示字符,所以 Base64 字符串的传输性很好,通常被用来“以字符串的形式保存或传输一些原始二进制数据”。

Base64 编码过程

由于 Base64 的字符集里只有 64 个字符,所以表示一个 Base64 字符只需要 6 bit 的空间。编码时原始二进制的内容(如下图绿色部分所示)就会每 6 bit 转化成一个 Base64 字符(如下图红色部分所示)。如果最后多出来几个单独的 bit,则尾部补 0 凑齐 6 bit。又因为字节是处理数据的最小单元,所以通常是一次性取 3 字节的数据转成 4 个 Base64 字符。假如最后多出来 1 个字节,则这个单独的字节只能编码出 2 个字符,通常这时会在尾部再补 2 个 “=” 符号,以使得 Base64 字符串的总长度是 4 的整数倍,同样的,如果最后多出来 2 字节的数据,则只能编码成 3 个 Base64 字符,尾部也会补 1 个 “=” 符号。下图中绿色的二进制内容编码出的 Base64 字符串就是 '2D3euQ==' 了。

image

由于编码后的字符串还是按照其他字符编码方式进行保存或传输的,比如 ASCII、UTF8 等,所以一个字符至少得占 8 bit 的空间,但这个字符实际只表示了原始数据里 6 bit 的内容。所以用 Base64 字符串来保存或传输原始二进制内容会导致所需的空间变大。

Base64 解码过程

解码是编码的逆过程。所以需要每 4 个字符凑出一段 24 bit 的原始内容,即每次还原 3 字节。如果最后多出来 2 个字符(不算尾部的 “=” 符号),说明原始的二进制数据后面有 1 个单独的字节,用这 2 个字符还原出最后的那个字节就行了。如果最后多出来 3 个字符,则把这 3 个字符还原成最后的那 2 个字节就行了。由编码过程可知最后是不可能只多出来 1 个字符的。

使用场景

1. DataURL

浏览器加载一些小文件时,如果每次都发起一个 HTTP 请求,其实是很浪费网络资源的,所以主流浏览器都支持了 DataURL 协议,即通过直接内嵌 Base64 字符串来表示资源本身的二进制数据。

格式为 data:[<mediatype>][;base64],<data>,最后的 <data> 就是 Base64 字符串。

比如把下面这段 DataURL 直接输入到浏览器地址栏回车,就可以看到他代表的原始 Unicode 字符串 "👻base64-pro🤗" 了:

data:text/plain;charset=utf-8;base64,8J%2BRu2Jhc2U2NC1wcm%2Fwn6SX
image

注意 <data> 的部分其实还在 Base64 字符串 的基础上做了一次 encodeURIComponent

再比如下面这段“内联”的 JS 脚本,它不需要在 script 标签的 src 属性里放一个 HTTP 链接。

<script src="data:text/plain;charset=utf-8;base64,YWxlcnQoJ%2FCfmIonKQ%3D%3D"></script>
image

再比如下面这个“内联”的图片,它也不需要在 img 标签的 src 属性里放一个图片链接。

<!-- 内容太长省略了 -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." />
image

2. 加密

Base64 字符串本身没啥加密性可言,它只是一种用字符串表示二进制内容的手段罢了,用的字符集以及编解码过程都是公开的。这里所说的加密应用是指一些加密场景中通常会用 Base64 字符串来传输数据。这是因为 Base64 里的字符都是 ASCII 字符,传输性很好,假如用 Unicode 字符来表示原始二进制内容,很可能由于传输过程中对字符的错误编解码导致数据损坏。

比如微信小程序中获取用户信息 时微信 API 返回的 encryptedData iv 就是原始数据套了一层 Base64 编码,这是为了保证把这段数据以字符串形式给我们时,或者我们再把这段数据发给自己的服务端进行解密时,这段数据不会损坏。

结语

我自己用 JS 写了一个 Base64 编解码的小工具——base64-pro,它支持将任意二进制数据或者 “Unicode 字符串” 与 Base64 字符串互转,也支持直接把这些数据生成 DataURL。可以通过 npm 安装或直接下载 JS 文件 使用,也正因为是纯 JS 写的,所以浏览器和 Node.js 环境下都可以用。比如上面的那几个小 DEMO,使用 base64-pro 来做就变得很简单了。

const Base64 = require("base64-pro");
const b64 = new Base64();
b64.bufferToBase64(new Uint8Array([0b11011000, 0b00111101, 0b11011110, 0b10111001])); // '2D3euQ=='
b64.strToBase64("👻base64-pro🤗");
b64.strToDataURL("alert('😊')");
b64.bufferToDataURL(require("fs").readFileSync("github.png"), "image/png");