Base64编码方式简介
我们直接用文本编辑器去打开pdf,exe等,肯定会看到很多乱码,因为并不是所有的二进制字符都可以打印出来。
为了能够打印出来所有的二进制数据,就用到了Base64编码。
Base64编码中,以每6个bit为一个单元,所以一共可以取64个值,对应64个可打印字符。这样3个Byte就相当于24bit,对应4个Base64单元。
例如编码Man,转化为Base64的编码就是
文本 | M | a | n | |||||||||||||||||||||
ASCII编码 | 77 | 97 | 110 | |||||||||||||||||||||
二进制位 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
索引 | 19 | 22 | 5 | 46 | ||||||||||||||||||||
Base64编码 | T | W | F | u |
所以Man经过Base64编码之后就是TWFu。
Base64的索引表如下
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
字符 | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X |
索引 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
字符 | Y | Z | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v |
索引 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ||||||||
字符 | w | x | y | z | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | + | / |
补位
从Base64的介绍可以看出,是每3个字符被编码成4个Base64个字符,那如果原始长度不能被3整除怎么办,那就补位,使用0字节在末尾补足,使其能够被3整除。在编码后的Base64为本后加上一个或者两个=号,代表补足的字节数。
如A经过编码只有就是QQ==,BC编码之后就是QkM=
Base64 变种
- For Mime:在MIME格式的电子邮件中,Base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。Mime Base64规定每行不超过76字符,超过后,自动使用\r\n进行分割
- For Url: URL Base64编码可用来在HTTP环境下传递较长的标识信息。然后,标准的Base64并不适合直接放在URL中传输,因为URL编码器会把标准的Base64中的/和+字符编程%xx的形式,而这些%号在存入数据库时还需要进行转换,因为ANSI SQL已经将%作为通配符。为了解决这个问题,可以采用一种用于URL的Base64编码,它不将原始Base64中的+和/替换成了!和-。
Java\kotlin中的使用
代码如下
class LxcTest {
@Test
fun base64() {
val source = "abcdefcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa你好🙆🙆🙆【♦⑸±"
println(source)
val after = Base64.getEncoder().encode(source.toByteArray())
println(String(after))
println(Base64.getEncoder().encodeToString(source.toByteArray()))
println(Base64.getMimeEncoder().encodeToString(source.toByteArray()))
println(Base64.getUrlEncoder().encodeToString(source.toByteArray()))
}
}
打印结果如下
abcdefcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa你好🙆🙆🙆【♦⑸±
YWJjZGVmY2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWHkvaDlpb3wn5mG8J+ZhvCfmYbjgJDimabikbjCsQ==
YWJjZGVmY2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWHkvaDlpb3wn5mG8J+ZhvCfmYbjgJDimabikbjCsQ==
YWJjZGVmY2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh
YWFhYWFhYWFhYWHkvaDlpb3wn5mG8J+ZhvCfmYbjgJDimabikbjCsQ==
YWJjZGVmY2FhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWHkvaDlpb3wn5mG8J-ZhvCfmYbjgJDimabikbjCsQ==
手撸一下编码
代码如下
package com.plbear.lxc.utils
import kotlin.experimental.and
/**
* Base64加密算法
* Created by yanyongjun on 1/28/21
*/
object Base64 {
private val encodeArray = charArrayOf(
'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', '+', '/'
)
fun Byte.toMyInt(): Int {
return if (this > 0) this.toInt() else ((0b0111_1111).toByte() and this).toInt() + 0b1000_0000
}
fun encodeByBase64(source: ByteArray): String {
if (source.isEmpty()) return ""
val groupCnt = source.size / 3
val otherCnt = source.size % 3
val result = StringBuilder()
for (i in 0 until groupCnt) {
val a = source[i * 3].toMyInt()
val b = source[i * 3 + 1].toMyInt()
val c = source[i * 3 + 2].toMyInt()
val index1 = a ushr 2
val index2 = ((a and 0b11) shl 4) or (b ushr 4)
val index3 = ((b and 0b1111) shl 2) or (c ushr 6)
val index4 = c and 0b11_1111
result.append(encodeArray[index1])
result.append(encodeArray[index2])
result.append(encodeArray[index3])
result.append(encodeArray[index4])
}
if (otherCnt == 1) {
val a = source[groupCnt * 3].toMyInt()
result.append(encodeArray[a ushr 2])
result.append(encodeArray[(a and 0b11) shl 4])
result.append("==")
} else if (otherCnt == 2) {
val a = source[groupCnt * 3].toMyInt()
val b = source[groupCnt * 3 + 1].toMyInt()
result.append(encodeArray[a ushr 2])
result.append(encodeArray[((a and 0b11) shl 4) or (b ushr 4)])
result.append(encodeArray[((b and 0b1111 shl 2))])
result.append("=")
}
return result.toString()
}
}