2

Base64编码是一种将字节数据编码为字符串的编码,字节数据会被编码成由64个可打印ASCII字符组成的字符串,这64个字符包括大写字母A-Z, 小写字母a-z, 以及数字 0 -9再加上 + 和 / ,刚好64个字符。对应的字符表如下图:
image.png

base64编码的一个用途就是对http的头信息进行编码,由于http头信息使用ASCII编码,如果包含特殊字符可能会导致头信息解析异常,采用base64编码保证头信息只包含一些简单字符,提高了安全性。前端在显示图片元素时也经常会遇到base64编码的图片资源。

那么base64具体是如何进行编码的呢?

编码方式很简单,就是对目标字节中的每六个bit位表示为字母表中的某个字符,例如:

‘abc’对应的二进制字节为: 01100001 , 01100010 , 01100011 ; 每六位进行分组得到的结果如下:

011000, 010110, 001001, 100011,转化为10进制就是:24,22, 9, 35,根据上面的字母表得到各个数字对应的字符为:YWJj,所以abc最终会被编码为 ‘YWJj’。由于三个字节最终被编码成了四个字节的字符串,所以长度增加了1/3.

看着这里大家可能会有一个问题,假设原数据的字节长度不是3的倍数,就会有剩余的bit位不够6个字符,这是就会涉及到填充的问题,填充就是在原数据后面加上额外的冗余位,使数据的bit位长度刚好能被24(也就是3个字节)整除(6和8的最小公倍数)。具体的填充规则可以简单的表述为: 任何完全填充(不包含原始数据中的位) 的 6 位组都由特殊的第 65 个符号“=” 表示。 如果 6 位组是部分填充的, 就将填充位设置为 0(http权威指南)。

举个例子:假设在编码的过程中原数组有四个字节,这时就需要再填充2个字节。假设最后的两个bit为是10,填充后的数据的最后几位如下:

10 xxxx, xxxxxx,xxxxxx (x代表填充位)

根据前面描述的填充规则:第一个6位组对应的数字为 10 0000,后面两个由于时完全填充的,被编码为==,最终的结果的后三位就变成了‘g==’。

在浏览器端,可以调用全局的方法 atob 和 btoa 实现二进制字符与base64编码的字符之间的互相转化。下面给出一个base64编码前端实现的简单例子:

const table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

// 参数为待编码的字符串

async function toBase64(str) {

// 获取utf-8编码字符串的二进制字节数据,也可以使用TextEncoder API

const blob = new Blob([str], { type: 'text/plain' });

// 获取字节长度,也可以使用第三方库提供的同步方法

// 这里为了演示简单

const srcBuf = await blob.arrayBuffer();

// 获取原数据的字节长度

const length = blob.size;

// 计算需要填充的位数

const padLen = length % 3 === 1

? 2

: length % 3 === 2 ? 1 : 0;

// 最终的长度

const size = length + padLen;

const arrBuf = new ArrayBuffer(size);

const srcArray = new Uint8Array(srcBuf);

const dstArray = new Uint8Array(arrBuf);

for (let i = 0; i < size; i++) {

if (i < length) {

dstArray[i] = srcArray[i];

} else {

dstArray[i] = 0x00;

}

}

let result = [];

// 每次处理3个字节

for (let i = 0; i < size; i += 3) {

// 取出三个字节

const a = dstArray[i];

const b = dstArray[i + 1];

const c = dstArray[i + 2];

// 最后一组

const isLast = i + 3 > length;

console.log(padLen)

result.push(a >> 2); // 第一个字节的前6位

result.push(((a & 0b00000011) << 4) | (b >> 4)); // 第一个字节的后两位加上第二个字节的前四位

// 第二个字节的后四位加上的三个字节的前两位

if (!isLast || padLen === 0) {

result.push(((b & 0b00001111) << 2) | (c >> 6));

result.push(c & 0b00111111); // 第三个字节的后六位

} else {

if (padLen === 2) {

result.push('=', '=');

} else if (padLen === 1) {

result.push(((b & 0b00001111) << 2) | (c >> 6), '=');

}

}

}

result = result.map(code => code === '=' ? '=' :        table[code]).join('');

return result;

}

base64转二进制字节原理类似,有兴趣的童鞋可以自行尝试!!
实际的运行效果如下图:

image.png


helloweilei
130 声望2 粉丝

菜鸟一枚,各位道友多多指教!