Base64编码原理及应用

作者:JAY 2018-06-12

前言

Base64是一种用可见字符传输二进制数据的编码方式。本文介绍Base64编码的原理,以及在web中的应用。


简介

有些场合,我们需要储存和发送文件,又不方便直接使用二进制流。这时候,我们需要把二进制数据翻译成成字符串,写进代码或XML里。ASCII编码虽然也能做到,但因为含有无法显示和打印字符,不便于阅读和传输。(你可以用记事本打开一个jpg格式的图片,看看这堆乱码。)

我们期望编码的结果可控、可复制、可打印、方便阅读,最好只包含大小写字母、数字、一些简单的符号如+、/和=。于是有了Base64。

有些场合,非ASCII字符容易出现编码问题,也可以使用Base64。比如,你可以对中文进行编码:

//unicode:
你只要尝试过飞,日后走路时也会仰望天空

//base64:
5L2g5Y+q6KaB5bCd6K+V6L+H6aOe77yM5pel5ZCO6LWw6Lev5pe25Lmf5Lya5Luw5pyb5aSp56m6

这种编码方式并不是为了安全,而是为了显示。因此,编码结果是唯一的,也是可逆的。


编码原理

编码的时候,取3字节的数据,放入一个24位的缓冲区,先来的字节占高位。这24位数据,每次取6位,按照下表转化为字符。

valuechar
valuechar
valuechar
valuechar

0

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

于是,缓冲区这3个字节数据,编码成了4位字符。举个例子,对笔者的名字进行编码如下:

文本JAY
ASCII编码74
6589
二进制位010010100100000101011001
Base64索引1836525
Base64编码S
kFZ

最终编码为:SkFZ。所有数据按照以上方式,3个字节为一组进行编码转换。如果要编码的字节数不能被3整除,则余下的位数补0,并在编码结果最后附加等号“=”来表示。剩余两个八位字节时附上两个等号,剩余一个八位字节时附上一个等号,具体如下表:

文本A

二进制位010000010000000000000000
Base64编码QQ==
文本BC
二进制位010000100100001100000000
Base64编码Q
kM=


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()。



参考文献

  1. https://en.wikipedia.org/wiki/Base64




本文未经许可禁止转载,如需转载请联系 JAY@oonne.com


TOP