目录前言base64 由来base64 的编码原理base64 编码对照表base64 的编码转换规则base64 优缺点javascript 的 base64 转码方法WEB ap
平静之下,蓦然回首,base64 却在灯火阑珊处。
今天翻开旧项目发现挺多图片相关的插件都是用 base64 来显示图片的。谈到 base64,脑海遐想翩翩,思绪回荡之下 base64 瑕瑜互见。本文旨在记录工作中遇见的问题并加以总结,如有不妥请指正~
base64 是网络传输 8Bit 字节代码的编码方式之一,是一种基于 64 个可打印字符来表示二进制数据的方法。在做支付系统时,报文交互都需要使用 base64 对明文进行转码,然后再进行签名或加密,之后再进行(或再次 base64 转码)传输。那么,base64 到底起到什么作用呢?
在参数传输的过程中经常遇到的一种情况:使用全英文的字符串没问题,但一旦涉及到中文就会出现乱码的情况。与此类似,网络上传输的字符并不全是可打印的字符,比如二进制文件、图片等。base64 的出现就是为了解决此问题,它是基于 64 个可打印的字符来表示二进制的数据的一种方法。
电子邮件刚问世的时候,只能传输英文,但后来随着用户的增加,中文、日韩俄文等文字的用户也有需求,但这些字符并不能被服务器或网关有效处理,因此 base64 就登场了。随后,base64 在 URL、Cookie、网页传输少量二进制文件中也有相应的使用。
基于a-z
、A-Z
、0-9
、+/
这 64 个字符来标识二进制数据,另外=
符号用于当字节缺位时补用。
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 要求把每三个 8Bit
的字节转换
为四个 6Bit
的字节(3*8 = 4*6 = 24
),然后把 6Bit再添两位高位 0
,组成四个 8Bit 的字节(4*8=32
)。
为什么使用 3 个字节一组呢?因为 6 和 8 的最小公倍数
为 24,三个字节正好 24 个二进制位,每 6 个 bit 位一组,恰好能够分为 4 组。
同时用于分组后每组添加两个高位 0,转换后
的字符串理论上
将要比原来的字符长 1/3(24/32=1/3)
。
步骤分解:
将待转换的字符串每三个字符分为一组,每个字符字节占 8bit,那么共有 24 个二进制位。
将 24 个二进制位每 6 个字节为一组,共分为 4 组。
在每组 6 字节前面添加两个 0,每组由 6 字节变为 8 字节二进制位,组成总共 32 个二进制位,即四个字节。
根据 base64 编码对照表获得对应的值。
举个栗子
LJY
为例。LJY
对应的 ASCII 码值分别为 76、74、89,对应的二进制值是 01001100、01001010、01011001。由此组成一个 24 位的二进制位字符串。
将 24 位的二进制位字符串,按照每 6 位二进制位一组分成 4 组。
对 4 组 每组 6 位二进制位字符串前面补两个 0,每组扩展为 8 位二进制位,4 组共扩展成 32 个二进制位,此时 4 组二进制位分别为:00010011、00000100、00101001、00011001。其对应的 base64 编码索引为:19、4、41、25。
用 base64 编码索引值在 base64 编码表中进行查找,分别对应:T、E、p、Z。
LJY
base64 编码之后就变为:TEpZ。| 文本 | L | J | Y |
| ASCII | 76 | 74 | 89 |
| 二进制位 | 01001100 | 01001010 | 01011001 |
| 分组二进制 | 010011 | 000100 | 101001 | 011001 |
| 分组二进制补2个0 | 00010011 | 00000100 | 00101001 | 00011001 |
| 分组索引 | 19 | 4 | 41 | 25 |
| base64编码 | T | E | p | Z |
主要展示:
转换前二进制位: 01001100 01001010 01011001
转换后二进制位: 00010011 00000100 00101001 00011001
复制代码
上述栗子是面向刚好三个字符为一组的情况。当然不是所有时候都这么巧字符位数足够,除此以外有位数不足的情况。那么,面对字符位数不足的情况下该如何处理呢?
base64 给出的方案是,当每组字符不足三位时,不足位数位置需要使用=
符号补上。
位数不足情况处理情景:
=
补上。=
补上。位数不足图解如下:
<!-- 缺2位字符,字符串以A为例转换base64后位QQ== -->
| 文本(1Byte) | A | | |
| 二进制位 | 01000001 | | |
| 分组二进制 | 010000 | 010000 | | |
| 分组二进制补0 | 00010000 | 00010000 | | |
| 分组索引 | 16 | 16 | | |
| base64编码 | Q | Q | = | = |
<!-- 缺1位字符,字符串以AB为例转换base64后位QUI= -->
| 文本(1Byte) | A | B | |
| 二进制位 | 01000001 | 01000010 | |
| 分组二进制 | 010000 | 010100 | 001000 | |
| 分组二进制补0 | 00010000 | 00010100 | 00001000 | |
| 分组索引 | 16 | 20 | 8 | |
| base64编码 | Q | U | I | = |
复制代码
列举了一个字符到三个字符转换为 base64 ,可以发现将 base64 就是按照 base64 编码对照表来将二进制转换为字符串,使得数据不能直接明文展示出来,但也算不上是加密,而这巧好可用在传输、存储、表示二进制领域的情景。
知道 base64 是什么后,也该到为什么出现了。为什么要是使用 base64 呢,这要从其优缺点入手来选择适合场景了。
enCodedData,是一个通过 btoa() 方法编码的字符串, 为二进制字符串包含 base64 编码的数据。并返回包含来自 encodedData 的解码数据的 ASCII 字符串。
stringToEncode 为要编码的二进制字符串。并返回包含 stringToEncode 的 base64 表示形式的 ASCII 字符串。
另外在 JavaScript 中,字符串使用 UTF-16 字符编码表示:在这种编码中,字符串表示为 16 位(2 字节)单元的序列。每个 ASCII 字符都可以放入其中一个单元的第一个字节,但许多其他字符不能。
base64 在设计上需要二进制数据作为其输入。就 JavaScript 字符串而言,这意味着每个字符只占用一个字节的字符串。因此,如果将一个字符串传递到 btoa()中,其中包含占用多个字节的字符,则会出现错误,因为这不被视为二进制数据,因此超 16 位字符在使用 btoa()时需要先对字符转码为二进制位。
// 简单数据
const encodedData = btoa('Hello, world'); // encode a string
const decodedData = atob(encodedData); // decode the string
// convert a Unicode string to a string in which
// each 16-bit unit occupies only one byte
function toBinary(string) {
const codeUnits = new Uint16Array(string.length);
for (let i = 0; i < codeUnits.length; i++) {
codeUnits[i] = string.charCodeAt(i);
}
const charCodes = new Uint8Array(codeUnits.buffer);
let result = '';
for (let i = 0; i < charCodes.byteLength; i++) {
result += String.fromCharCode(charCodes[i]);
}
return result;
}
function fromBinary(binary) {
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
const charCodes = new Uint16Array(bytes.buffer);
let result = '';
for (let i = 0; i < charCodes.length; i++) {
result += String.fromCharCode(charCodes[i]);
}
return result;
}
// a string that contains characters occupying > 1 byte
const myString = '☸☹☺☻☼☾☿';
const converted = toBinary(myString);
const encoded = btoa(converted);
console.log(encoded); // OCY5JjomOyY8Jj4mPyY=
const decoded = atob(encoded);
const original = fromBinary(decoded);
console.log(original); // ☸☹☺☻☼☾☿
// base64编码表
const map = {
0: 52,
1: 53,
2: 54,
3: 55,
4: 56,
5: 57,
6: 58,
7: 59,
8: 60,
9: 61,
A: 0,
B: 1,
C: 2,
D: 3,
E: 4,
F: 5,
G: 6,
H: 7,
I: 8,
J: 9,
K: 10,
L: 11,
M: 12,
N: 13,
O: 14,
P: 15,
Q: 16,
R: 17,
S: 18,
T: 19,
U: 20,
V: 21,
W: 22,
X: 23,
Y: 24,
Z: 25,
a: 26,
b: 27,
c: 28,
d: 29,
e: 30,
f: 31,
g: 32,
h: 33,
i: 34,
j: 35,
k: 36,
l: 37,
m: 38,
n: 39,
o: 40,
p: 41,
q: 42,
r: 43,
s: 44,
t: 45,
u: 46,
v: 47,
w: 48,
x: 49,
y: 50,
z: 51,
'+': 62,
'/': 63,
};
function base64to2(base64) {
let len = base64.length * 0.75; // 转换为int8array所需长度
base64 = base64.replace(/=*$/, ''); // 去掉=号(占位的)
const int8 = new Int8Array(len); //设置int8array视图
let arr1,
arr2,
arr3,
arr4,
p = 0;
for (let i = 0; i < base64.length; i += 4) {
arr1 = map[base64[i]]; // 每次循环 都将base644个字节转换为3个int8array直接
arr2 = map[base64[i + 1]];
arr3 = map[base64[i + 2]];
arr4 = map[base64[i + 3]];
// 假设数据arr 数据 00101011 00101111 00110011 00110001
int8[p++] = (arr1 << 2) | (arr2 >> 4);
// 上面的操作 arr1向左边移动2位 变为10101100
// arr2 向右移动4位:00000010
// | 为'与'操作: 10101110
int8[p++] = (arr2 << 4) | (arr3 >> 2);
int8[p++] = (arr3 << 6) | arr4;
}
return int8;
}
// base64图片转blob
function base64toBlob(base64) {
var arr = base64.split(','),
mime = arr[0].match(/:(.*?);/)[1] || 'image/png',
bstr = atob(arr[1]), // 将base64转为Unicode规则编码
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n); // 转换编码后才可以使用charCodeAt 找到Unicode编码
}
return new Blob([u8arr], { type: mime });
}
function base64ToBlob(base64) {
var arr = base64.split(',');
var mime = arr[0].match(/:(.*?);/)[1] || 'image/png';
// 去掉url的头,并转化为byte
var bytes = window.atob(arr[1]);
// 处理异常,将ascii码小于0的转换为大于0
var ab = new ArrayBuffer(bytes.length);
// 生成视图(直接针对内存):8位无符号整数,长度1个字节
var u8arr = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
u8arr[i] = bytes.charCodeAt(i);
}
return new Blob([u8arr], { type: mime });
}
到此这篇关于一文讲清base64编码原理的文章就介绍到这了,更多相关base64编码内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: 一文讲清base64编码原理
本文链接: https://lsjlt.com/news/197933.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0