一个 javascript 的 Base64转换库 @cnwhy/base64

项目要用到 Base64, 在网上找了好几个用着都者不是特别爽, 索性写一个.

项目地址

GitHub: cnwhy/Base64.js

安装

1
npm i @cnwhy/base64

为何重复造轮子?

  1. btoa , atob 只支持 Latin1 字符.
  2. Base64编/解码本该与字符串无关, 但几乎所有 Base64 的decode方法都输出字符串, 限制了使用场景.

    本库的 decode() 方法单纯将 Base64 字符串解析为Uint8Array; 但重写了返回字节数组的toString()方法, 默认以UTF-8编码解析为字符串(类似 nodejsbuffer 对像的toString()).

  3. ++常用的的 Base64 编码库处理 javascript 字符串时会主动替换错误(空)编码字符, 导致解码的数据与原数据不一至.++
    比如用 nodejs 中的 Buffer:

    1
    2
    3
    4
    var s = '\ud800'; //U+d800 属于代理区, 是一个空码, 如果从utf16来看,是一个4字节字符的一半.
    var b64 = Buffer.from(s).toString('base64');
    var _s = Buffer.from(b64, 'base64').toString();
    console.log(s == _s); //false

    本库对于字符串的转换默认用UTF-8编码, 但无视无效符(解码按同一规则), 保证 js 的字符串(UCS-2)可以无损转换.

  4. 其它 Base64 库 固定了 Base64 编码表, 与字符串的编码方式;

    本库抽象出 Base64 算法, 支持自定义的 编码表 和 字符串编码方式, 适应更多特殊场景;

  5. 多数项目只需要 encodedecode , 而我又不想copy代码.

    本库有ES6风格模块文件, 利用 webpackrollup 可打出最简包.

兼容性

nodejs , 浏览器通用;

不支持ArrayBuffer的环境将会用Array代替Uint8Array.

demo

字符串 编码,解码

1
2
3
4
5
6
7
8
9
10
import { encode, decode } from '@cnwhy/base64';
let str = '中国\u{10121}美国'; //中国𐄡美国 注: 𐄡是一个4字节字符
let b64_str = encode(str);
let _str = decode(b64_str).toString();
console.log([str, b64_str, str == _str].join('\n'));
/*
中国𐄡美国
5Lit5Zu98JCEoee+juWbvQ==
true
*/

编码 Uint8Array ArrayBuffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { encode, decode } from '@cnwhy/base64';
let arr = [1,2,3,4,5];
let bytes = new Uint8Array([1, 2, 3, 4, 5]);
let buffer = Buffer.from([1, 2, 3, 4, 5]);
[arr,bytes,buffer,].forEach((v)=>{
let b64 = encode(v);
let u8 = decode(b64);
console.log(Array.from(v).toString());
console.log(b64.toString());
console.log(Array.from(u8).toString());
console.log('--');
})
/*
1,2,3,4,5
AQIDBAU=
1,2,3,4,5
--
1,2,3,4,5
AQIDBAU=
1,2,3,4,5
--
1,2,3,4,5
AQIDBAU=
1,2,3,4,5
--
*/

decode 函数始终 Uint8Array 对像,不支持 Uint8Array 的环境返回 Array

自定义码表与字符串编码方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定义一个看起来乱码的 Base64 转换方法, 并且 字符串用 UTF16 编码;
import { createEncode , createDecode } from '@cnwhy/base64';
const TABLE = 'xQh}s7*y~A|nkj4Bf%z1R,P+)mMS{(&EWCKegp6r!OX</LuY-l9^ZJ#cTU[vHda$'; // base64码表, 不重复的64个ascii字符
const PAD = '.'; // 补位符, 一个ascii字符
const Utf16Encode = function(str) {
let cods = str.split('').map(s => s.charCodeAt(0));
return new Uint8Array(new Uint16Array(cods).buffer);
}
const Utf16Decode = function(arr) {
let u16 = Array.from(new Uint16Array(arr.buffer));
return u16.map(c => String.fromCharCode(c)).join('');
}
//自定义base64 转换方法
const myEncode = createEncode(TABLE,PAD,Utf16Encode);
const myDecode = createDecode(TABLE,PAD,Utf16Decode);
let str = '中国\u{10121}美国';
let b64_str = myEncode(str);
let _str = myDecode(b64_str).toString();
console.log([str, b64_str, str == _str].join('\n'));
/*
中国𐄡美国
nRvd,W})~(#4E$JP
true
*/

API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Base64 = {
BASE64_TABLE: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
BASE64_URL_TABLE: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
PAD: "=";
// UTF8 编码 解码; 可做为 strEncode strDecode 参数;
utf8Encode(str:string):Uint8Array|number[];
utf8Decode(utf8arr:Uint8Array|number[]):string;

//Base64 编码 解码
encode(input:string|ArrayBuffer|Uint8Array|number[]):string;
decode(base64str: string) => number[]|Uint8Array;

//适用于URL的Base64 编码 解码( "_" "-" 替换 "/" "+");
encodeURL(input:string|ArrayBuffer|Uint8Array|number[]):string;
decodeURL(base64str: string) => number[]|Uint8Array;

//创建自定义Base64 encode , decode 函数
createEncode(strEncode: Function): (input: any) => string;
createEncode(table?: string[] | string, pad?: string, strEncode?: Function): (input: any) => string;
createDecode(strDecode: Function): (base64str: string) => Uint8Array | number[];
createDecode(table?: string[] | string, pad?: string, strDecode?: Function): (base64str: string) => Uint8Array | number[];
}