Base64概述
Base64就是选出64个字符:a-z、A-Z、0-9、+、/(再加上作为垫字的"=",实际上是65个字符),作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。
转换方式可以分为四步:
- 第一步,将每三个字节作为一组,一共是24个二进制位。
- 第二步,将这24个二进制位分为四组,每个组有6个二进制位。
- 第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节。
- 第四步,根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y
因为Base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右。
示例
单词Man转成Base64编码:
- 第一步,“M”、“a”、"n"的ASCII值分别是77、97、110,对应的二进制值是01001101、01100001、01101110,将它们连成一个24位的二进制字符串010011010110000101101110。
- 第二步,将这个24位的二进制字符串分成4组,每组6个二进制位:010011、010110、000101、101110。
- 第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节:00010011、00010110、00000101、00101110。它们的十进制值分别是19、22、5、46。
- 第四步,根据上表,得到每个值对应Base64编码,即T、W、F、u。
字节数不足三
二个字节
二个字节的情况:将这二个字节的一共16个二进制位,按照上面的规则,转成三组,最后一组除了前面加两个0以外,后面也要加两个0。这样得到一个三位的Base64编码,再在末尾补上一个"="号。
一个字节
一个字节的情况:将这一个字节的8个二进制位,按照上面的规则转成二组,最后一组除了前面加二个0以外,后面再加4个0。这样得到一个二位的Base64编码,再在末尾补上两个"="号。
Java实现
public class MyBase64 {
private static final char[] CHS = {
'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', '+', '/'};
private static final Map<Byte, Byte> CHS_MAP = new HashMap<>(64);
static {
for (int i = 0; i < CHS.length; i++) {
CHS_MAP.put((byte) CHS[i], (byte) i);
}
}
public static byte[] encode(byte[] bytes) {
int groupCount = bytes.length / 3;
int remainCount = bytes.length % 3;
int encLength;
if (remainCount == 0) {
encLength = groupCount * 4;
} else {
encLength = (groupCount + 1) * 4;
}
int index, encI;
byte b1, b2, b3;
byte[] encBuf = new byte[encLength];
for (int i = 0; i < groupCount; i++) {
index = i * 3;
b1 = bytes[index];
b2 = bytes[index + 1];
b3 = bytes[index + 2];
encI = i * 4;
encBuf[encI] = (byte) CHS[(b1 >> 2) & 0x3F];
encBuf[encI + 1] = (byte) CHS[((b1 << 4) & 0x30) | ((b2 >> 4) & 0x0F)];
encBuf[encI + 2] = (byte) CHS[((b2 << 2) & 0x3C) | ((b3 >> 6) & 0x03)];
encBuf[encI + 3] = (byte) CHS[b3 & 0x3F];
}
if (remainCount == 2) {
index = groupCount * 3;
b1 = bytes[index];
b2 = bytes[index + 1];
b3 = 0;
encI = groupCount * 4;
encBuf[encI] = (byte) CHS[(b1 >> 2) & 0x3F];
encBuf[encI + 1] = (byte) CHS[((b1 << 4) & 0x30) | ((b2 >> 4) & 0x0F)];
encBuf[encI + 2] = (byte) CHS[((b2 << 2) & 0x3C) | ((b3 >> 6) & 0x03)];
encBuf[encI + 3] = '=';
}
if (remainCount == 1) {
index = groupCount * 3;
b1 = bytes[index];
b2 = 0;
encI = groupCount * 4;
encBuf[encI] = (byte) CHS[(b1 >> 2) & 0x3F];
encBuf[encI + 1] = (byte) CHS[((b1 << 4) & 0x30) | ((b2 >> 4) & 0x0F)];
encBuf[encI + 2] = encBuf[encI + 3] = '=';
}
return encBuf;
}
public static byte[] decode(byte[] encBytes) {
int encLen = encBytes.length;
int groupCount = encLen / 4;
int decLen;
int remainCount;
if (encBytes[encLen - 1] == '=') {
if (encBytes[encLen - 2] == '=') {
decLen = groupCount * 3 - 2;
remainCount = 1;
} else {
decLen = groupCount * 3 - 1;
remainCount = 2;
}
} else {
decLen = groupCount * 3;
remainCount = 0;
}
int index, index2;
byte[] decBytes = new byte[decLen];
byte b1, b2, b3;
for (int i = 0; i < groupCount - 1; i++) {
decodeGroup(encBytes, decBytes, i);
}
if (remainCount == 2) {
index = (groupCount - 1) * 4;
b1 = CHS_MAP.get(encBytes[index]);
b2 = CHS_MAP.get(encBytes[index + 1]);
b3 = CHS_MAP.get(encBytes[index + 2]);
index2 = (groupCount - 1) * 3;
decBytes[index2] = (byte) (((b1 << 2) & 0xFF) | ((b2 >> 4) & 0xFF));
decBytes[index2 + 1] = (byte) (((b2 << 4) & 0xFF) | ((b3 >> 2) & 0xFF));
}
if (remainCount == 1) {
index = (groupCount - 1) * 4;
b1 = CHS_MAP.get(encBytes[index]);
b2 = CHS_MAP.get(encBytes[index + 1]);
index2 = (groupCount - 1) * 3;
decBytes[index2] = (byte) (((b1 << 2) & 0xFF) | ((b2 >> 4) & 0xFF));
}
if (remainCount == 0) {
decodeGroup(encBytes, decBytes, groupCount - 1);
}
return decBytes;
}
private static void decodeGroup(byte[] encBytes, byte[] decBytes, int i) {
byte b1, b2, b3, b4;
int index = i * 4;
b1 = CHS_MAP.get(encBytes[index]);
b2 = CHS_MAP.get(encBytes[index + 1]);
b3 = CHS_MAP.get(encBytes[index + 2]);
b4 = CHS_MAP.get(encBytes[index + 3]);
int index2 = i * 3;
decBytes[index2] = (byte) (((b1 << 2) & 0xFF) | ((b2 >> 4) & 0xFF));
decBytes[index2 + 1] = (byte) (((b2 << 4) & 0xFF) | ((b3 >> 2) & 0xFF));
decBytes[index2 + 2] = (byte) (((b3 << 6) & 0xFF) | (b4 & 0xFF));
}
}