Base64 编码浅析

Base64 编码浅析

Base64 常用于 Data URL 的内联中,这篇博客文章探讨两个问题:

  1. 为什么图片转成 Base64 编码,就可以直接内联到 HTML 中显示呢?
  2. 为什么 Base64 编码后,体积会增大 1/3 呢?

为什么 Base64 编码可以内联到 HTML 中?

我们知道 HTTP 协议是文本协议,不同于常规的二进制协议那样直接进行二进制传输。Base64 编码是从二进制到字符的过程,可用于在 HTTP 环境下传递较长的标识信息。

什么是 Base64 编码

首先 Base64 是一种编码算法,为什么叫做 Base64 呢?其实原因也很简单,是因为该算法共包含 64 个字符。包括大小写拉丁字母各 26 个、数字 10 个、加号 + 和斜杠 /,共 64 个字符。此外还有等号 = 用来作为后缀用途。

字符与索引的对应关系如下图所示。

ValueCharValueCharValueCharValueChar
0A16Q32g48w
1B17R33h49x
3C18S34i50y
4D19T35j51z
5E20U36k520
6F21V37l531
7G22W38m542
8H23X39n553
9I24Y40o564
10J25Z41p575
11K26a42q586
12L27b43r597
13M28c44s608
14N29d45t619
14O30e46u62+
15P31f47v63/

但,为什么 Base64 编码算法只支持 64 个字符呢?

首先,我们先回顾下 ASCII 码。ASCII 码的范围是 0-127,其中 0-31 和 127 是控制字符,共 33 个。其余 95 个,即 32-126 是可显示字符,包括数字、大小写字母、常用符号等。如下图所示(数据来自 维基百科):

二进制十进制十六进制缩写Unicode
表示法
脱出字符
表示法
名称/意义
0000 0000000NUL^@空字符(Null)
0000 0001101SOH^A标题开始
0000 0010202STX^B本文开始
0000 0011303ETX^C本文结束
0000 0100404EOT^D传输结束
0000 0101505ENQ^E请求
0000 0110606ACK^F确认回应
0000 0111707BEL^G响铃
0000 1000808BS^H退格
0000 1001909HT^I水平定位符号
0000 1010100ALF^J换行键
0000 1011110BVT^K垂直定位符号
0000 1100120CFF^L换页键
0000 1101130DCR^MCR (字符)
0000 1110140ESO^N取消变换(Shift out)
0000 1111150FSI^O启用变换(Shift in)
0001 00001610DLE^P跳出数据通讯
0001 00011711DC1^Q设备控制一
(XON 激活软件速度控制)
0001 00101812DC2^R设备控制二
0001 00111913DC3^S设备控制三
(XOFF 停用软件速度控制)
0001 01002014DC4^T设备控制四
0001 01012115NAK^U确认失败回应
0001 01102216SYN^V同步用暂停
0001 01112317ETB^W区块传输结束
0001 10002418CAN^X取消
0001 10012519EM^Y连线介质中断
0001 1010261ASUB^Z替换
0001 1011271BESC^[退出键
0001 1100281CFS^\文件分割符
0001 1101291DGS^]组群分隔符
0001 1110301ERS^^记录分隔符
0001 1111311FUS^_单元分隔符
0111 11111277FDEL^?删除

ASCII 中 32-126 可显示字符表:

二进制十进制十六进制图形
0010 00003220(space)
0010 00013321!
0010 00103422"
0010 00113523#
0010 01003624$
0010 01013725%
0010 01103826&
0010 01113927'
0010 10004028(
0010 10014129)
0010 1010422A*
0010 1011432B+
0010 1100442C,
0010 1101452D-
0010 1110462E.
0010 1111472F/
0011 000048300
0011 000149311
0011 001050322
0011 001151333
0011 010052344
0011 010153355
0011 011054366
0011 011155377
0011 100056388
0011 100157399
0011 1010583A:
0011 1011593B;
0011 1100603C<
0011 1101613D=
0011 1110623E>
0011 1111633F?
0100 00006440@
0100 00016541A
0100 00106642B
0100 00116743C
0100 01006844D
0100 01016945E
0100 01107046F
0100 01117147G
0100 10007248H
0100 10017349I
0100 1010744AJ
0100 1011754BK
0100 1100764CL
0100 1101774DM
0100 1110784EN
0100 1111794FO
0101 00008050P
0101 00018151Q
0101 00108252R
0101 00118353S
0101 01008454T
0101 01018555U
0101 01108656V
0101 01118757W
0101 10008858X
0101 10018959Y
0101 1010905AZ
0101 1011915B[
0101 1100925C|
0101 1101935D]
0101 1110945E^
0101 1111955F_
0110 00009660`
0110 00019761a
0110 00109862b
0110 00119963c
0110 010010064d
0110 010110165e
0110 011010266f
0110 011110367g
0110 100010468h
0110 100110569i
0110 10101066Aj
0110 10111076Bk
0110 11001086Cl
0110 11011096Dm
0110 11101106En
0110 11111116Fo
0111 000011270p
0111 000111371q
0111 001011472r
0111 001111573s
0111 010011674t
0111 010111775u
0111 011011876v
0111 011111977w
0111 100012078x
0111 100112179y
0111 10101227Az
0111 10111237B{
0111 11001247C|
0111 11011257D}
0111 11101267E~

早期的一些传输协议,例如邮件传输协议 SMTP,只能传输可打印的 ASCII 字符。这样原本的 8bit 字节码(0-255)就会超出使用范围,从而到这无法传输。

这时,就产生了 Base64 编码,Base64 编码利用 6bit 字符来表达原本的 8bit 字符

Base64 编码原理

上面我们知道了什么是 Base64 编码,知道了其包含的 64 个字符。它主要是通过 6bit 字符来表达原本的 8bit 字符。接下来我们一起学习下这一过程是如何进行的。

首先,6bit 显然不够容纳 8bit 的数据。6 和 8 的最小公倍数是 24,所以我们用 4 个 Base64 字符刚好能够表示三个传统的 8bit 字符。如下所示,字符串 Man 的编码图解如下:

字符串 Man 的 base64 编码图

Man 的编码结果为 TWFu,显然,Base64 编码会多 1/3 的长度,这也解释了文中开头的疑问,为什么 Base64 编码后的体积会大 1/3。

Man 这个字符串的长度刚好是 3,我们能用 4 个 Base64 来表示。如果待编码的字符串长度不是三的倍数时应该怎么处理呢?

这是需要做一个特殊处理,假设带编码字符串长度为 10。这前 9 个字符可以用 12 个 Base64 字符表示。第 10 个字符的前 6bit 作为一个 Base64 字符,剩下的 2bit 后面需要先补 0,补到 6 位(此处补 4 个 0)作为第二个 Base64 字符,至于第三个和第四个 Base64 字符,虽然没有相对应的内容,我们仍需以 = 填充。

如下图所示,A 对应的 Base64 编码为 QQ==,BC 对应的 Base64 编码为 QkM=。

字符串 A 和 BC 的 base64 编码图

最后的问题就是解码啦,解码的过程比较简单。去掉末尾的等号 =。剩下的 Base64 字符,每 8bit 组成一个 8bit 字节,最后剩余不足 8 位的丢弃即可。

总结

本文篇幅较短,旨在简单介绍 Base64 编码原理。相信看完之后,大家一定能够理解为什么 Base64 编码后体积会增大 1/3,而不再是死记硬背这一特点。

备注:
完整的 ASCII 编码表,详见
USASCII code chart

本文作者 verymuch,转载请注明来源链接:

原文链接:https://git.io/Je18f

本文链接:https://tie.pub/2019/09/base64-encode-plain/