目录
- 一、介绍
- 1. Base64 的工作原理
- Base64 字符集
- Base64 编码基本原理
- Base64 编码具体解释
- 2. Base64 的编码示例
- 3. Base64 的应用
- 3.1 在 URL 中嵌入数据
- 3.2 电子邮件附件
- 3.3 数据传输
- 3.4 存储与缓存
- 总结
- 4. 在 JavaScript 中使用 Base64
- 编码
- 解码
- 使用 `Buffer` (Node.js)
一、介绍
Base64 是一种数据编码方式,用于将二进制数据转换为可打印的 ASCII 字符串。这种编码方式广泛用于在文本系统中传输二进制数据,比如在电子邮件中嵌入图像,或者在网络协议中传输复杂的数据。
// 123456
// 编码后
// MTIzNDU2
//hello
//编码后
//aGVsbG8=
1. Base64 的工作原理
Base64 字符集
Base64 字符集是一种用于将二进制数据编码为可打印的 ASCII 字符串的标准字符集。它包含 64 个字符,这些字符包括字母、数字以及两个特殊字符(通常是 +
和 /
,在 URL 和文件系统中有时会用不同的字符,如 -
和 _
)。
Base64 字符集的字符分布如下:
- 大写字母(A-Z):A 到 Z,代表值 0 到 25。
- 小写字母(a-z):a 到 z,代表值 26 到 51。
- 数字(0-9):0 到 9,代表值 52 到 61。
- 特殊字符:
+
代表值 62。/
代表值 63。
这些字符用于表示 6 位二进制数。具体的字符集如下(在大多数 Base64 实现中):
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
Base64 编码基本原理
Base64 编码将每 3 个字节的原始二进制数据转换为 4 个可打印字符。
这是因为每个 Base64 字符表示 6 位数据(2^6 = 64),即 000000~111111
,代表0~63
,而 3 个字节是 24 位(3*8 = 24)。将 24 位数据分成 4 组,每组 6 位,从而产生 4 个 Base64 字符。
- 原始数据:每 3 个字节 = 24 位。
- Base64 编码:每 4 个字符 = 24 位。
Base64 编码具体解释
让我们更详细地解释一下 Base64 编码的过程,特别是为什么 3 个字节的二进制数据会转换为 4 个 Base64 字符。
-
字节与位的关系:
- 1 字节 = 8 位(bits)。
- 3 个字节 = 3 * 8 = 24 位。
-
Base64 字符表示:
- Base64 编码将二进制数据分成 6 位一组进行处理,因为 Base64 字符集包含 64 个不同的字符(2^6 = 64)。所以,每个 Base64 字符表示 6 位数据。
-
编码过程:
-
步骤 1: 从 3 个字节的二进制数据中提取 24 位数据。
-
步骤 2: 将这 24 位数据分成 4 组,每组 6 位。
-
步骤 3: 每 6 位数据通过 Base64 编码表(包含 64 个字符)映射成一个 Base64 字符。这就是为什么 3 个字节会被编码为 4 个 Base64 字符。
-
2. Base64 的编码示例
假设我们要编码字符串 “hello”:
-
原始数据:
- “hello” 的 ASCII 编码是
104 101 108 108 111
。
- “hello” 的 ASCII 编码是
-
转换为二进制:
104
=01101000
101
=01100101
108
=01101100
108
=01101100
111
=01101111
-
分组:
- 每 6 位分成一组,例如:
011010 000110 010101 101100 011011 001111
- 每 6 位分成一组,例如:
-
编码:
- 这些 6 位组对应 Base64 字符,例如:
011010 = 26 = a 000110 = 6 = G 010101 = 21 = V 101100 = 44 = s 011011 = 27 = b 001111 = 15 = P
- 最终编码结果是 “aGVsbG8=”。
- 这些 6 位组对应 Base64 字符,例如:
注意:Base64 编码的结果可能会在末尾添加一个或两个 =
符号作为填充字符,用于确保数据长度是 4 的倍数。
3. Base64 的应用
下面是针对不同场景的 Base64 编码和解码示例,包括 URL 嵌入、电子邮件附件、数据传输和存储与缓存。我们将使用 JavaScript 代码来演示这些操作。
3.1 在 URL 中嵌入数据
场景描述:将图像数据通过 Base64 编码嵌入到 URL 中,例如用作 src
属性的值。
代码示例:
假设我们有一个小的 PNG 图像文件,我们将其转为 Base64 字符串,然后在 HTML 中使用 Data URL 进行嵌入。
// 图像数据转换为 Base64 字符串的示例
function encodeImageToBase64(imageFile) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(imageFile);
});
}
// 假设 imageFile 是 File 对象,例如通过 file input 获取的图像文件
const imageFile = document.querySelector('input[type="file"]').files[0];
encodeImageToBase64(imageFile).then(base64String => {
console.log(base64String); // 输出 Base64 编码字符串
// 使用 Base64 字符串作为图像的 src
document.querySelector('img').src = base64String;
});
Base64 字符串示例:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAAC8GO2jAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHwAAAABJRU5ErkJggg==
3.2 电子邮件附件
场景描述:在电子邮件中嵌入图像或文档附件,Base64 编码用于将二进制数据嵌入邮件内容。
代码示例:
// 将文件转换为 Base64 字符串
function convertFileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// 假设 file 是要发送的文件,例如通过 file input 获取的文件
const file = document.querySelector('input[type="file"]').files[0];
convertFileToBase64(file).then(base64String => {
// 在邮件中嵌入 Base64 数据
const emailBody = `<img src="${base64String}" alt="Embedded Image">`;
console.log(emailBody); // 输出邮件内容,通常由邮件客户端进行发送
});
Base64 字符串示例:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAAAAAAAD/2wBDAAoHBwkHBgoJCAkLCwoMDxkQDw4ODxwQEBwTGBYWFx0dIyIoIyIiIyIhJi4kIyIoNjo2LCYgJSYlLCYsMTExLCsyLC4vJjAqMygyNjo2N0FfJSMfFjM1M0YwYTMzLV0uMz5iFfAvfFwmMfJwNWUuLzP/2wBDAQsLC8rLC8wLjQ4MzUyMj8vM2QwP0PCZFBZXk5eXF9fX13lHUkLAw8xPD5dZHiMmYPk9fXYnKk2YJLc6MMPS9eK0YXEro6XjptH5ae/f1sZYqFjpLgWCyMuJUdFCOMqKIlC5Ti8gHjK0ePoNBe8aFSih+KTcCnEEw3zXyRYmrDmfMTlI6MYDFX/l5Y5r/mWq2Bf8XBLZfD4uYl2ziFthP6oyW8Onxw5WzNGxkL77B4u3D0+4ys...
3.3 数据传输
场景描述:通过 API 或网络协议将二进制数据转换为 Base64 字符串并进行传输。
代码示例:
// 将二进制数据编码为 Base64 字符串
function arrayBufferToBase64(arrayBuffer) {
let binary = '';
let bytes = new Uint8Array(arrayBuffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
// 假设我们从 API 获取到的二进制数据
fetch('https://example.com/api/data')
.then(response => response.arrayBuffer())
.then(arrayBuffer => {
const base64String = arrayBufferToBase64(arrayBuffer);
console.log(base64String); // 输出 Base64 编码字符串
// 将 Base64 字符串发送到服务器或其他应用
});
Base64 字符串示例:
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAAC8GO2jAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHwAAAABJRU5ErkJggg==
延伸:
让我们详细解释一下为什么对从 API 获取的二进制数据进行上述格式转换:
- 从 API 获取响应数据
当你使用 fetch
API 请求数据时,响应可以是多种格式,包括文本、JSON、Blob、ArrayBuffer 等。fetch
的默认响应格式是 ReadableStream
,需要进一步处理来获取具体的数据格式。对于二进制数据,response.arrayBuffer() 是一种常见的选择。
- 转换为 ArrayBuffer
ArrayBuffer
是一种通用的、固定长度的原始二进制数据缓冲区。在 Web API 中,response.arrayBuffer()
方法将响应的原始数据转换为 ArrayBuffer
对象,这样可以更方便地处理二进制数据。
fetch('https://example.com/api/data')
.then(response => response.arrayBuffer())
.then(arrayBuffer => {
// arrayBuffer 现在是原始的二进制数据
});
注意,Blob
和 ArrayBuffer
都是 Web API 中用于处理二进制数据的两种重要数据结构,它们各自具有不同的用途和特性。主要区别在于:
- 数据处理方式:ArrayBuffer 用于低级别的二进制数据处理,依赖视图(如 Uint8Array)来访问数据。Blob 用于高层次的数据处理,支持文件和流式操作。
- 数据大小:ArrayBuffer 适用于处理固定大小的二进制数据。Blob 适用于处理任意大小的二进制数据块,可以是非常大的数据。
- 数据用途:ArrayBuffer 通常用于处理来自网络请求的二进制数据,适合数据转换和操作。Blob 通常用于处理文件上传、下载和生成内容,适合文件和流的操作。
- ArrayBuffer 转换为 Base64 字符串
ArrayBuffer
本身是二进制的,而 Base64 是一种文本编码方式,用于将二进制数据转换为字符串格式。Base64 编码的好处包括:
- 可嵌入文本环境:Base64 编码的字符串可以在 HTML、JSON、XML 等文本格式中嵌入和传输。
- 数据传输:在某些协议或服务中,Base64 编码的文本数据更易于处理和传输。
转换步骤:
function arrayBufferToBase64(arrayBuffer) {
let binary = '';
let bytes = new Uint8Array(arrayBuffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
- ArrayBuffer 转 Uint8Array:
new Uint8Array(arrayBuffer)
Uint8Array
是一种视图,它允许我们以字节为单位访问ArrayBuffer
的数据。
- Uint8Array 转 Binary String:
String.fromCharCode()
- 将
Uint8Array
的每个字节转换为字符,并将这些字符组合成一个二进制字符串。因为 btoa 要求入参是二进制字符串。
- 将
- Binary String 转 Base64:
window.btoa()
- 使用
window.btoa()
方法将二进制字符串转换为 Base64 编码的字符串。
- 使用
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAADZ+foAAAAJcEhZcwAACxMAAAsTAdjz8gAAAHSiVHBtSFiaL3diNS1zYWUtYm5vODYAAAAyAAAAYAAAAPbAAAAASUVORK5CYII='
总结,每一步的转换都有其特定的用途:
- ArrayBuffer 提供了对原始二进制数据的访问。
- Uint8Array 使我们可以以字节为单位处理数据。
- Binary String 是一种在 JavaScript 中处理二进制数据的中间格式。
- Base64 编码将二进制数据转换为可嵌入文本环境中的字符串格式,便于传输和存储。
这些转换步骤确保了我们能够有效地处理和传输 API 返回的二进制数据,并将其转换为可以在不同场景中使用的格式。
3.4 存储与缓存
场景描述:将二进制数据(如图像)编码为 Base64 字符串并存储在浏览器的本地存储中。
代码示例:
// 将图像数据存储到本地存储
function storeImageInLocalStorage(imageFile) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result;
localStorage.setItem('storedImage', base64String);
resolve();
};
reader.onerror = reject;
reader.readAsDataURL(imageFile);
});
}
// 从本地存储中读取图像数据并显示
function displayStoredImage() {
const base64String = localStorage.getItem('storedImage');
if (base64String) {
document.querySelector('img').src = base64String;
}
}
// 假设 imageFile 是 File 对象,例如通过 file input 获取的图像文件
const imageFile = document.querySelector('input[type="file"]').files[0];
storeImageInLocalStorage(imageFile).then(() => {
displayStoredImage();
});
Base64 字符串示例(存储在本地存储中):
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAAC8GO2jAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHwAAAABJRU5ErkJggg==
总结
这些示例展示了如何在不同场景中使用 Base64 编码进行数据转换和处理:
- URL 中嵌入数据:将 Base64 编码的数据直接嵌入到 URL 中,如图像的
src
属性。 - 电子邮件附件:将二进制文件(如图像或文档)编码为 Base64 字符串,然后在电子邮件中嵌入。
- 数据传输:将二进制数据编码为 Base64 字符串,通过 API 或网络协议进行传输。
- 存储与缓存:将 Base64 编码的数据存储在浏览器的本地存储中,用于后续的读取和显示。
4. 在 JavaScript 中使用 Base64
编码
const str = "hello";
const encoded = btoa(str); // "aGVsbG8="
console.log(encoded);
MDN - btoa
解码
const encoded = "aGVsbG8=";
const decoded = atob(encoded); // "hello"
console.log(decoded);
MDN - atob
使用 Buffer
(Node.js)
在 Node.js 中,你可以使用 Buffer
对象来进行 Base64 编码和解码:
// 编码
const buffer = Buffer.from('hello');
const encoded = buffer.toString('base64'); // "aGVsbG8="
console.log(encoded);
// 解码
const decodedBuffer = Buffer.from(encoded, 'base64');
const decoded = decodedBuffer.toString(); // "hello"
console.log(decoded);