共计 3786 个字符,预计需要花费 10 分钟才能阅读完成。
一. 简介
首先有个小 Tips,1 个字母字符 = 1 个字节 (byte) = 8 位 (bit), 这是表示单位.
Base64 是网络上最常见的用于传输 8bit 的编码方式之一,Base64 就是一种基于 64 个可打印字符来表示二进制数据的方法。
在一些网络传送渠道, 有时候有些的字节字符不能被支持. 比如图片的二进制流的每个字节不可能全部是可见字符, 这种情况下传送不了. 而 Base64 的机制就能很好解决这种问题, 它不改变原来的协议, 在原来的基础做一种扩展, 基于 64 个可打印字符来表示二进制.
再者, 有时候我们通过记事本去打开一些图片或者应用程序, 会得到一大堆看不懂的乱码, 因为二进制文件里面有很多无法显示和打印的字符. 所以如果要让记事本能够处理二进制数据, 就可以使用 Base64 来进行转码, 将不可见的转成可见的.
二. 算法原理
我们拿到一串字符, 比如 abcdef. 先按 ASCII 码编码.
它会将字符串按字节数分开, 每三个字节为一组. 3 byte * 8 bit === 24bit.
在这组中, 将 24 位数据按照每 6 位为一组, 再次分成 4 组.
然后将这四组的 6 位数的高位各补两个 0, 将其转为十进制数.
进行查表, 得到对应的字符, 就是对应的 Base64 转化的字符.
那么有个问题来了, 如果原始数据的位数不是 3 的整数倍怎么办,Base 会在不足的后面用 ”=” 补足, 所以这个 ”=” 只会存在在 Base64 码的最后, 并且只会有 1 或者 2 个, 不可能出现在中间.
// javaScript 实现
if (!Shotgun)
var Shotgun = {};
if (!Shotgun.Js)
Shotgun.Js = {};
Shotgun.Js.Base64 = {
_table: [
‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’,
‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’, ‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’,
‘g’, ‘h’, ‘i’, ‘j’, ‘k’, ‘l’, ‘m’, ‘n’, ‘o’, ‘p’, ‘q’, ‘r’, ‘s’, ‘t’, ‘u’, ‘v’,
‘w’, ‘x’, ‘y’, ‘z’, ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’, ‘+’, ‘/’
],
encode: function (bin) {
var codes = [];
var un = 0;
un = bin.length % 3;
if (un == 1)
bin.push(0, 0);
else if (un == 2)
bin.push(0);
for (var i = 2; i < bin.length; i += 3) {
var c = bin[i – 2] << 16;
c |= bin[i – 1] << 8;
c |= bin[i];
codes.push(this._table);
codes.push(this._table);
codes.push(this._table);
codes.push(this._table);
}
if (un >= 1) {
codes[codes.length – 1] = “=”;
bin.pop();
}
if (un == 1) {
codes[codes.length – 2] = “=”;
bin.pop();
}
return codes.join(“”);
},
decode: function (base64Str) {
var i = 0;
var bin = [];
var x = 0, code = 0, eq = 0;
while (i < base64Str.length) {
var c = base64Str.charAt(i++);
var idx = this._table.indexOf(c);
if (idx == -1) {
switch (c) {
case ‘=’: idx = 0; eq++; break;
case ‘ ‘:
case ‘\n’:
case “\r”:
case ‘\t’:
continue;
default:
throw {“message”: “\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u65E0\u6548\u7F16\u7801\uFF1A” + c};
}
}
if (eq > 0 && idx != 0)
throw {“message”: “\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u7F16\u7801\u683C\u5F0F\u9519\u8BEF\uFF01”};
code = code << 6 | idx;
if (++x != 4)
continue;
bin.push(code >> 16);
bin.push(code >> 8 & 0xff);
bin.push(code & 0xff)
code = x = 0;
}
if (code != 0)
throw {“message”: “\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u7F16\u7801\u6570\u636E\u957F\u5EA6\u9519\u8BEF”};
if (eq == 1)
bin.pop();
else if (eq == 2) {
bin.pop();
bin.pop();
} else if (eq > 2)
throw {“message”: “\u0062\u0061\u0073\u0065\u0036\u0034\u002E\u0074\u0068\u0065\u002D\u0078\u002E\u0063\u006E\u0020\u0045\u0072\u0072\u006F\u0072\u003A\u7F16\u7801\u683C\u5F0F\u9519\u8BEF\uFF01”};
return bin;
}
};
三. 应用
Base64 编码可用于在 HTTP 环境下传递较长的标识信息.
电子邮件: 有些文本协议不支持不可见字符的传递,只能用大于 32 的可见字符来传递信息
Base64 也会经常用作一个简单的“加密”来保护某些数据,而真正的加密通常都比较繁琐.
垃圾讯息传播者用 Base64 来避过反垃圾邮件工具,因为那些工具通常都不会翻译 Base64 的讯息.
前端在实现页面一些比较小的图片,通常会选择将图片内容直接内嵌在页面中,避免不必要的外部资源加载,增大页面加载时间,但是图片数据是二进制数据,该怎么嵌入呢?绝大多数现代浏览器都支持一种名为 Data URLs 的特性,允许使用 Base64 对图片或其他文件的二进制数据进行编码,将其作为文本字符串嵌入网页中。比如 webpack 工具中的 url-loader 默认将 8kb 以下的图片编译成 Base64 码嵌在引用文件中.
四. 前端应用
crypto-js
功能强大, 不止处理 Base64, 支持的模块非常多,star 数目前 6k+, 前后端可用
// 后端 npm 包管理
npm install crypto-js
import sha256 from ‘crypto-js/sha256’;
import hmacSHA512 from ‘crypto-js/hmac-sha512’;
import Base64 from ‘crypto-js/enc-base64’;
const message, nonce, path, privateKey; // …
const hashDigest = sha256(nonce + message);
const hmacDigest = Base64.stringify(hmacSHA512(path + hashDigest, privateKey));
// 前端 Brower 管理
bower install crypto-js
js-Base64
专门处理 Base64, 目前 star 数 2k+, 前后端均可用
// 安装
$ npm install –save js-base64
// 如果你使用的 es6 语法, 需要转载这个进行语法转换
$ npm install –save babel-preset-env
//node.js
var Base64 = require(‘js-base64’).Base64;
//es6+
import {Base64} from ‘js-base64’;
// 非常轻快的使用体验
Base64.encode(‘dankogai’); // ZGFua29nYWk=
Base64.encode(‘ 小飼弾 ’); // 5bCP6aO85by+
Base64.encodeURI(‘ 小飼弾 ’); // 5bCP6aO85by
五. 解密
如果你看懂了以上的算法原理, 那么解密其实不是太大的问题, 只要你遵循这个规则进行逆推运算, 就可以得到原始数据. 所以严格上说 Base64 并不是什么加密, 只是编译过后的并不是明文而已