Base64详解与JavaScript中的使用


简介

Base64 是一种用于传输 8Bit 字节码的编码方式,其可以将任意的二进制数据转换为可打印的 ASCII 字符串,用于在网络传输中方便的传输或存储数据。

一个常见的误区是将 Base64 看作为加密算法,实际上 Base64 只是一种编码方式,不具备加密功能。

相较于 ASCII 编码的使用 7Bit7 个二进制位)字节码来表示一个字符,Base64 使用 6Bit 字节码表示一个字符,即 2^6=64 个不同的字符。
这些字符包括 26 个大写英文字符、26 个小写英文字符、10 个数字字符和 2 个特殊字符 +/= 用于填充)。

Base64 编码表如下:

Value Encoding Value Encoding Value Encoding Value Encoding
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 /

编码步骤

若目标不是二进制数据,而是字符串,那么 Base64 首先将目标转换为二进制数据,这个步骤分为如下几步:

  1. 将字符串的每一个字符转换为其对应的 ASCII
  2. 对每一个 ASCII 码进行 2 进制转换,不足 8 位的前面补 0,得到 8 位的二进制数
  3. 将所有二进制数拼接在一起,得到一个长串的完整二进制数

这里以字符串 Mari 为例

1
2
3
4
Array.from( "Mari" )
.map( item => item.charCodeAt(0) ) // [77, 97, 114, 105]
.map( item => item.toString(2).padStart(8, "0") ) // ["01001101", "01100001", "01110010", "01101001"]
.join( "" ) // "01001101011000010111001001101001"

在得到二进制数据后,Base64 将其按照 6 位一组进行分割,分割后不足 6 位在后面补零。

根据上面得到的结果,分割后得到:

1
2
010011 010110 000101 110010 011010 010000
19 22 5 50 26 16

然后将每一位根据 Base64 编码表进行转换,得到:

1
TWFyaQ

Base64 编码后的字符串长度需要为 4 的倍数,如果不足则需要使用 = 补充,因此对上方的结果作最后补充处理,得到:

1
TWFyaQ==

这就是 Base64 的编码步骤,依次方式反向操作也可以将 Base64 编码的字符串解码为原始数据。

JavaScript 中的操作方法

JavaScript 为我们提供了 btoaatob 方法,用于进行 Base64 编码和解码。

1
2
btoa( "Mari" ) // "TWFyaQ=="
atob( "TWFyaQ==" ) // "Mari"

不过需要注意的是,btoa 方法不能处理由非 ASCII 码字符组成的字符串。在 ASCII 编码中,所有的字符均为单字节的。
但对于非 ASCII 字符例如汉字,其通常需要使用多个字节来表示,此时 btoa 方法会抛出异常。

1
2
3
4
5
"M".codePointAt( 0 ).toString( 2 ) // "1001101" 单字节字符
btoa( "M" ); // "TQ=="

"茉".codePointAt( 0 ).toString( 2 ) // "10011100100010101" 双字节字符
btoa( "茉" ); // Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

因此若需要处理非 ASCII 字符,需要在中间进行一次转换操作,例如使用 encodeURIComponentdecodeURIComponent 方法。

1
2
3
4
5
6
7
8
9
10
function base64Encode( str ) {
return btoa( encodeURIComponent( str ) );
}

function base64Decode( str ) {
return decodeURIComponent( atob( str ) );
}

base64Encode( "茉莉" ); // "JUU4JThDJTg5JUU4JThFJTg5"
base64Decode( "JUU4JThDJTg5JUU4JThFJTg5" ); // "茉莉"