2018-06-12
前言
Base64是一种用可见字符传输二进制数据的编码方式。本文介绍Base64编码的原理,以及在web中的应用。
简介
有些场合,我们需要储存和发送文件,又不方便直接使用二进制流。这时候,我们需要把二进制数据翻译成成字符串,写进代码或XML里。ASCII编码虽然也能做到,但因为含有无法显示和打印字符,不便于阅读和传输。(你可以用记事本打开一个jpg格式的图片,看看这堆乱码。)
我们期望编码的结果可控、可复制、可打印、方便阅读,最好只包含大小写字母、数字、一些简单的符号如+、/和=。于是有了Base64。
有些场合,非ASCII字符容易出现编码问题,也可以使用Base64。比如,你可以对中文进行编码:
//unicode: 你只要尝试过飞,日后走路时也会仰望天空 //base64: 5L2g5Y+q6KaB5bCd6K+V6L+H6aOe77yM5pel5ZCO6LWw6Lev5pe25Lmf5Lya5Luw5pyb5aSp56m6
这种编码方式并不是为了安全,而是为了显示。因此,编码结果是唯一的,也是可逆的。
编码原理
编码的时候,取3字节的数据,放入一个24位的缓冲区,先来的字节占高位。这24位数据,每次取6位,按照下表转化为字符。
value | char | value | char | value | char | value | char | |||
0 | A | 16 | Q | 32 | g | 48 | w | |||
1 | B | 17 | R | 33 | h | 49 | x | |||
2 | C | 18 | S | 34 | i | 50 | y | |||
3 | D | 19 | T | 35 | j | 51 | z | |||
4 | E | 20 | U | 36 | k | 52 | 0 | |||
5 | F | 21 | V | 37 | l | 53 | 1 | |||
6 | G | 22 | W | 38 | m | 54 | 2 | |||
7 | H | 23 | X | 39 | n | 55 | 3 | |||
8 | I | 24 | Y | 40 | o | 56 | 4 | |||
9 | J | 25 | Z | 41 | p | 57 | 5 | |||
10 | K | 26 | a | 42 | q | 58 | 6 | |||
11 | L | 27 | b | 43 | r | 59 | 7 | |||
12 | M | 28 | c | 44 | s | 60 | 8 | |||
13 | N | 29 | d | 45 | t | 61 | 9 | |||
14 | O | 30 | e | 46 | u | 62 | + | |||
15 | P | 31 | f | 47 | v | 63 | / |
于是,缓冲区这3个字节数据,编码成了4位字符。举个例子,对笔者的名字进行编码如下:
文本 | J | A | Y | |||||||||||||||||||||
ASCII编码 | 74 | 65 | 89 | |||||||||||||||||||||
二进制位 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 |
Base64索引 | 18 | 36 | 5 | 25 | ||||||||||||||||||||
Base64编码 | S | k | F | Z |
最终编码为:SkFZ。所有数据按照以上方式,3个字节为一组进行编码转换。如果要编码的字节数不能被3整除,则余下的位数补0,并在编码结果最后附加等号“=”来表示。剩余两个八位字节时附上两个等号,剩余一个八位字节时附上一个等号,具体如下表:
文本 | A | |||||||||||||||||||||||
二进制位 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Base64编码 | Q | Q | = | = |
文本 | B | C | ||||||||||||||||||||||
二进制位 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Base64编码 | Q | k | M | = |
Base64使用64个字符,因此每个字符最多表示6位数据(26=64)。每个字符在储存和传输时占用一个字节(8位),因此,从二进制编码为Base64,会增加至少三分之一的数据长度。
Base64的应用
DataUrl:
DataUrl 是一种将文件通过Base64编码写进网页源代码的技术,也是Base64最广泛的应用之一。你可以查看下面两张图的源代码,右边这张的src是正常的,而左边的src是以“data:image/jpeg;base64”开头的一段编码后的数据。
网站上使用图标、图片、字体文件,文件小而数量多,每个文件都占用一次完整http请求,拖累了整个网页加载的速度。这时候我们希望能直接在页面中嵌入这些资源,这就是DataUrl。除了上面例子的,在<img>标签的src中使用,也可以在CSS的background-image、@font-face的src使用,IE8以上都兼容。
不过,DataUrl也不能滥用。虽然减少了一次请求,但Base64编码的原理使得图片变得更大。同样参考上面的图片,原图大小14kb,编码之后19kb。而且,浏览器并不会对DataUrl传输的文件进行缓存,意味着每次加载页面都得重新传输。
链接:
举一个老司机最熟悉的,“迅雷下载专用地址”的例子。事实上,所谓的专用链接,不过是对真实下载地址前后分别添加AA和ZZ,然后使用Base64编码而来。举个例子,最新版本的Node.js的下载链接:
//1.原始链接: https://nodejs.org/dist/v8.11.2/node-v8.11.2-x64.msi //2.前后加AA和ZZ AAhttps://nodejs.org/dist/v8.11.2/node-v8.11.2-x64.msi //3.Base64编码 QUFodHRwczovL25vZGVqcy5vcmcvZGlzdC92OC4xMS4yL25vZGUtdjguMTEuMi14NjQubXNpWlo= //4.加上协议,得到迅雷下载链接 thunder://QUFodHRwczovL25vZGVqcy5vcmcvZGlzdC92OC4xMS4yL25vZGUtdjguMTEuMi14NjQubXNpWlo=
你可以使用上述编码后的链接下载到所需的文件。当然,以后在网上看到别人发的迅雷专用链接,你也可以解码得到原始下载链接。
JWT:
Base64编码的cookie或token,典型的代表就是JWT了,可以参考我另一篇博客《Json Web Token》。
其他:
MIME格式的邮件、微软的MHT、XML需要储存图片或文件的场景。这些格式都只能支持字符串,而又有使用图片和文件的需求,于是使用Base64进行编码。
编码和解码
Node.js可以使用Buffer读取二进制,当然也可以进行Base64编码和解码:
let b = new Buffer('JAY'); let s = b.toString('base64'); // SkFZ let b = new Buffer('SkFZ', 'base64') let s = b.toString(); // JAY
PHP自带编码解码函数:
echo base64_encode('JAY'); //SkFZ echo base64_decode('SkFZ'); //JAY
Python也有内置的方法:
>>> import base64 >>> base64.b64encode('JAY') 'SkFZ' >>> base64.b64decode('SkFZ') 'JAY'
javascript也有原生的atob()和btoa():
let encoded = btoa('JAY'); // 'SkFZ' let decoded = atob('SkFZ'); // 'JAY'
这两个函数只支持ASCII,并且不兼容IE6-9。虽然网上也有很多第三方库,但这里推荐使用polyfill兼容,对中文先encodeURIComponent编码后再btoa()。
参考文献
本文未经许可禁止转载,如需转载请联系 JAY@oonne.com