前端Base64编码知识
# Base64 起源
为什么会有 Base64 编码呢?
因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像 ASCII 码的控制字符就不能通过邮件传送。这样用途就受到了很大的限制,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。
最好的方法就是在不改变传统协议的情况下,做一种扩展方案来支持二进制文件的传送,把不可打印的字符也能用可打印字符来表示,问题就解决了。Base64 编码应运而生,Base64 就是一种基于 64 个可打印字符来表示二进制数据的表示方法。
# Base64 编码 64 的含义
所以说 base64 中 64 指的就是 64 个可打印字符(ASCII 编码)。
- A-Z 26
- a-z 26
- 0-9 10
+ /
2
# Base64 编码原理
因为 base64 编码只有 64 个字符,用 6 个比特表示就可以了,而正常英文字符是采用 ASCII 编码方式,一个字母占一个字节,即 8 个比特位,所以 base 编码时候,会将 3 字节转换为四字节,遇到转换过程中不够三字节时,会自己在后面补上=
,所以说编码后体积至少变大 1/3。
6 个有效 bit 来表示传统字符的 8 个 bit 呢?8 和 6 的最小公倍数是 24,也就是说 3 个传统字节可以由 4 个 Base64 字符来表示,保证有效位数是一样的,这样就多了 1/3 的字节数来弥补 Base64 只有 6 个有效 bit 的不足。
# Base64 优缺点
优点:
- 可以将二进制数据(比如图片)转化为可打印字符,方便传输数据;
- 对数据进行简单的加密,肉眼是安全的;
- 如果是在 html 或者 css 处理图片,可以减少 http 请求。
缺点:
- 内容编码后体积变大, 至少 1/3(因为是三字节变成四个字节,当只有一个字节的时候,也至少会变成三个字节);
- 编码和解码需要额外工作量。
# Base64 编码和解码
# Base64 应用场景
# 1. canvas 图片生成
canvas 的 toDataURL 可以把 canvas 的画布内容转 base64 编码格式包含图片展示的 data URI。
const ctx = canvasEl.getContext("2d");
// ...... other code
const dataUrl = canvasEl.toDataURL();
// data:image/png;base64,iVBORw0KGgoAAAANSUhE.........
2
3
4
5
# 2. 文件读取
FileReader 的 readAsDataURL 可以把上传的文件转为 base64 格式的 data URI,比较常见的场景是用户头像的剪裁和上传。
# 3. JWT
JWT 由 header, payload,signature 三部分组成,是通过base64进行的编码,前两个解码后,都是可以明文看见的。
# 4. 网站图片和小图片
移动端网站图标优化,还有小图片使用场景,比如img标签,背景图等。
img标签:
<img src="data:image/png;base64,iVBORw0KGgoAAAA......." />
css背景图:
.bg{
background: url(data:image/png;base64,iVBORw0KGgoAAAA.......)
}
2
3
# 5. 简单的数据加密
当然这不是好方法,但是至少让你不好解读。
# 补充 -- 前端编码知识
# 1. 比特和字节
比特又叫位。 在计算机的世界里,信息的表示方式只有 0 和 1, 其可以表示两种状态。一位二进制可以表示两状态, N 位可以表示 2^N 种状态。
一个字节(Byte)有 8 位(Bit),所以一个字节可以表示 2^8 = 256 种状态。
# 2. 获得字符的 Unicode 码点
String.prototype.charCodeAt()
方法可以获取字符的码点,获取范围为 0~65535,注意这里获取到的是 Unicode 的码点,不同的编码方式编码之后会得到不同的结果。
常见的编码方式是 UTF-8,还有 UTF-16、UTF-32,这几种编码方式都是基于 Unicode 字符集(全球通用的字符集合);还有 ASCII 编码是基于 ASCII 字符集(只有 128 个字符,欧美国家字符集合)的。其中 UTF-8 编码是兼容 ASCII 字符集的。这些编码原理可以参考我之前总结的文章:JavaScript 的字符编码 (opens new window)
# 3. 进制转换
10 进制转其他进制,可以通过 Number.prototype.toString(radix):
(100).toString(2); // 1100100
(100).toString(16); // 64, 也等于 ox64
2
其他进制转为 10 进制,可以通过 parseInt(string, radix),radix 表示当前字符串的进制:
parseInt("10000000", 2); // 128
parseInt("10", 16); // 16
2
这里额外提一下一元操作符号+
可以把字符串转为数字,后面也会用到,之前提到的 0b,0o,0x 这里都会生效。
+"1000" + // 1000
"0b10000000" + // 128
"0o10" + // 8
"0x10"; // 16
2
3
4
# 总结
说起 Base64 编码可能有些奇怪,因为大多数的编码都是由字符转化成二进制的过程,而从二进制转成字符的过程称为解码。而 Base64 的概念就恰好反了,由二进制转到字符称为编码,由字符到二进制称为解码。
Base64 编码主要用在传输、存储、表示二进制等领域,还可以用来加密,但是这种加密比较简单,只是一眼看上去不知道什么内容罢了,当然也可以对 Base64 的字符序列进行定制来进行加密。
Base64 编码是从二进制到字符的过程,像一些中文字符用不同的编码转为二进制时,产生的二进制是不一样的,所以最终产生的 Base64 字符也不一样。例如"上网"对应 utf-8 格式的 Base64 编码是"5LiK572R",对应 GB2312 格式的 Base64 编码是"yc/N+A=="。
参考文章:
前端 Base64 编码知识,一文打尽,探索起源,追求真相。 (opens new window)