最近项目需要用到 Base64, 想到npm中找个好用的, 很可惜没一个符合我的预期, 于自己捣鼓一个, 感觉还不错所以分享出来;
其实我的需求其实很简单:
- 浏览器可用; (利用 Buffer 的方法出局);
- 支持字符串 (现有的库都支持, 只有
btoa,atob只支持 Latin1) - javascript 字符串无损转换 (因为这一点, 现有库全军覆没), 稍后做说明;
- 能用上
Tree-shaking, 因为项目一般只用了Base64的一半功能(encode或decode), 我可不想copy代码;
为什么不能做到javascript字符串无损转换
看一个例子:
var s = '\ud800';
var b64 = Buffer.from(s).toString('base64');
var _s = Buffer.from(b64, 'base64').toString();
console.log(s == _s); //false
为什么最后是 false?
首先要知道, 字符串编码为base64之前要先转成字节数组, 字节数组再进行 base64 编码, 解码反之, 我们一般都用'utf8'编码字符串;
再看 U+d800 是一个空码, 从 utf16 来看, 是一个4字节字符的一半, 然而javascript是ucs2编码, 所以它在javascript中是一个正常的字符串;
Buffer 默认也是以'utf8'编码字符串 , 所以过程是这样 字符串>Unicode>utf8, 字符串 到 Unicode 过和程对于空码, 替换成了一个占位符 '�', 之后的转换过程就跟着错了, 还原回来当然就错了;
反正就认准一点,我一个字符串 出了个门回来就变了 这是不可以的.
怎么解决:
方案1:
直接用ucs2编码; 省去 字符串 到 Unicode 的过程, 自然不会出问题了;
var s = '\ud800';
var b64 = Buffer.from(s,'ucs2').toString('base64');
var _s = Buffer.from(b64, 'base64').toString('ucs2');
console.log(s == _s); //true
注意: 上面代码里的 'ucs2' 其实是 'utf16le' 的别名. 但有一个问题, 单字符串中 英文居多时 , 编码后的Base64会比用 "utf8" 的情况长很多;
方案2:
字符串>Unicode>utf8 的过程不把空码替换, 解码也一样, 这样也可以保证字符串的一至性;
我写的这个Base64库用的也是这个方案;
其它
除了上面说的5点, 我还做了一些顺手的功能;
- 抽象出 Base64 算法, 支持自定义的 编码表 和 字符串编码方式, 适应更多特殊场景;
- 支持字节数组 (既然在都有了 ArrayBuffer/Uint8Array 类型, 为什么不顺便支持一下, 其实比支持字符串更简单吧) , 其实我的项目也没用到;
GitHub: cnwhy/Base64.js
详细的使用方法可参看这篇