Base64 内容传输编码是什么?
用较官方的说法来讲,Base64编码被设计用来表示任意长度的8bit字节序列,它进行编码和解码的算法都很简单,编码后的数据不需要可读性,并且编码后的数据比未编码的数据始终大33%。简言之,Base64就是使用64个可打印字符来表示任意的二进制数据。
请参考RFC2045第6.8小节关于Base64编码的描述
RFC2045 Base64 Content Transfer Encoding
Base64编码原理
Base64的编码原理并不复杂,首先我们从US-ASCII编码表中取了65个可打印字符用作Base64的编码表,这样就允许我们只使用6bit(6位二进制数据)来表示这些字符,即2^6=64个。那么还有多出来的一个’=’字符当填充字符(后面解释我们为什么需要一个填充字符)来做补位,那么下面我先给出Base64的编码表,然后我们在看看怎么进行编码。
- The Base64 Alphabet Table
bin | hex | dec | character | bin | hex | dec | character | bin | hex | dec | character |
---|---|---|---|---|---|---|---|---|---|---|---|
0000 0000 | 0x00 | 0 | A | 0001 1010 | 0x1A | 26 | a | 0011 0100 | 0x34 | 52 | 0 |
0000 0001 | 0x01 | 1 | B | 0001 1011 | 0x1B | 27 | b | 0011 0101 | 0x35 | 53 | 1 |
0000 0010 | 0x02 | 2 | C | 0001 1100 | 0x1C | 28 | c | 0011 0110 | 0x36 | 54 | 2 |
0000 0011 | 0x03 | 3 | D | 0001 1101 | 0x1D | 29 | d | 0011 0111 | 0x37 | 55 | 3 |
0000 0100 | 0x04 | 4 | E | 0001 1110 | 0x1E | 30 | e | 0011 1000 | 0x38 | 56 | 4 |
0000 0101 | 0x05 | 5 | F | 0001 1111 | 0x1F | 31 | f | 0011 1001 | 0x39 | 57 | 5 |
0000 0110 | 0x06 | 6 | G | 0010 0000 | 0x20 | 32 | g | 0011 1010 | 0x3A | 58 | 6 |
0000 0111 | 0x07 | 7 | H | 0010 0001 | 0x21 | 33 | h | 0011 1011 | 0x3B | 59 | 7 |
0000 1000 | 0x08 | 8 | I | 0010 0020 | 0x22 | 34 | i | 0011 1100 | 0x3C | 60 | 8 |
0000 1001 | 0x09 | 9 | J | 0010 0011 | 0x23 | 35 | j | 0011 1101 | 0x3D | 61 | 9 |
0000 1010 | 0x0A | 10 | K | 0010 0100 | 0x24 | 36 | k | 0011 1110 | 0x3E | 62 | + |
0000 1011 | 0x0B | 11 | L | 0010 0101 | 0x25 | 37 | l | 0011 1111 | 0x3F | 63 | / |
0000 1100 | 0x0C | 12 | M | 0010 0110 | 0x26 | 38 | m | pad | pad | pad | = |
0000 1101 | 0x0D | 13 | N | 0010 0111 | 0x27 | 39 | n | ||||
0000 1110 | 0x0E | 14 | O | 0010 1000 | 0x28 | 40 | o | ||||
0000 1111 | 0x0F | 15 | P | 0010 1001 | 0x29 | 41 | p | ||||
0001 0000 | 0x10 | 16 | Q | 0010 1010 | 0x2A | 42 | q | ||||
0001 0001 | 0x11 | 17 | R | 0010 1011 | 0x2B | 43 | r | ||||
0001 0010 | 0x12 | 18 | S | 0010 1100 | 0x2C | 44 | s | ||||
0001 0011 | 0x13 | 19 | T | 0010 1101 | 0x2D | 45 | t | ||||
0001 0100 | 0x14 | 20 | U | 0010 1110 | 0x2E | 46 | u | ||||
0001 0101 | 0x15 | 21 | V | 0010 1111 | 0x2F | 47 | v | ||||
0001 0110 | 0x16 | 22 | W | 0011 0000 | 0x30 | 48 | w | ||||
0001 0111 | 0x17 | 23 | X | 0011 0001 | 0x31 | 49 | x | ||||
0001 1000 | 0x18 | 24 | Y | 0011 0010 | 0x32 | 50 | y | ||||
0001 1001 | 0x19 | 25 | Z | 0011 0011 | 0x33 | 51 | z |
有了上面的编码表,我们就可以开始处理数据呢,对于输入的数据来讲,我们以24-bit(3个连续的8-bit字节)为一组作为输入,将输入的24-bit数据,按照从左到右的顺序,分成连续的4组6-bit的数据,每组6-bit数据可以看作是Base64编码表中的索引,查表后我们可以得到4个可打印字符作为输出。下面以”cat”字符串为例示范。
假设当前字符串编码集是单字节的ascii编码,我们以字符串’cat’为例。
那么我们可以得到字符串’cat’的base64编码结果是’Y2F0’,把一个24-bit的数据编码成了4个字符。各种编程语言都提供了Base64编码的实现。你可以在浏览器控制台中用JS验证下结果是否正确。
1 | btoa('cat') //output: Y2F0 |
- 特殊的情况
当然,我们不可能所有的数据的长度都是24的整数倍,如果我们按照规则进行编码操作,剩下的最后一组数据长度是小于24-bit的话,我们应该如何处理呢?
一般我们是直接在右边补上0bit位来组成一个完整的24-bit的输入组,这样我们还是可以按前面定义的规则划分成4个6-bit的组来进行编码,那么就剩下两种需要填充字符串补位的情况:
(1)如果最后一个输入长度是8-bit的话,那相当于我们要补16-bit才能凑成一组24-bit一组的输入,最后编码完成后的输出是2个正常的编码字符,2个’=’填充字符。这样我们在解码时就知道我们填充了多少位的数据。
(2)如果最后一个输入长度是16-bit的话,那就是说我们需要补充8-bit的来凑一组24-bit的输入,最后编码完成后的输出是3个正常的编码字符和1个’=’填充字符。
基于上面描述的两种情况,我们继续在浏览器中验证一下1
2
3
4
5
6// last input 8-bit fewer than 24-bit, need two pad character
btoa('catt') //output: Y2F0dA==
atob('Y2F0dA==') // output: catt
// last input 16-bit fewer than 24-bit, need one pad character
btoa('catty') //output: Y2F0dHk=
atob('Y2F0dHk=') // output: catty