前言
浏览中提供了许多使用 Javascript 操作二进制数据的 api 方法,初学 JS 时难免会傻傻分不清彼此。本文将一一介绍这些常见方法的基本定义和用法,希望对你有所帮助~
基础 Api 介绍
Blob
官方文档:developer.mozilla.org/en-US/docs/…
Blob 是 JavaScript 中用于处理不可变的二进制数据对象,尤其在处理文件和数据流时非常有用。Blob (Binary Large Object) 可以用来创建、读取和操作二进制数据,比如图像、音频、视频文件等。
Blob 对象的创建主要使用 new Blob() 构造函数:
const blob = new Blob([data], { type: 'mime/type' });
data是包含数据的数组,可以是字符串、数组缓冲区(ArrayBuffer)、TypedArray等。type是 MIME 类型,用于描述数据的格式,比如image/jpeg,text/plain等。
示例:
const text = "Hello, world!";
const blob = new Blob([text], { type: 'text/plain' });
ArrayBuffer
官方文档:developer.mozilla.org/en-US/docs/…
ArrayBuffer是 JavaScript 中用于处理二进制数据的对象,尤其适用于处理大规模的原始二进制数据,例如在 Web 应用中传输和解析图像、音频、视频文件,或进行网络通信时的数据处理。ArrayBuffer不能直接操作二进制数据,但可以通过视图(如TypedArray和DataView)进行访问。
与 Blob 的区别
上文中提到可以用 ArrayBuffer 来创建 Blob 对象,那么它们既然都表示二进制数据,那么区别是什么呢?
一句话概括:ArrayBuffer 是可变的二进制缓冲区,而 Blob 是不可变的,如果要修改 Blob 的数据就只能新建一个 Blob 对象
用途上的区别:
ArrayBuffer:主要用于内存中的二进制数据操作。它允许创建可变大小的缓冲区,以字节为单位分配内存,并通过 TypedArray 和 DataView 视图来读取和写入其中的数据。ArrayBuffer 更接近底层,可以用来操作和解析复杂的二进制数据。
Blob:是不可变的二进制数据的高层封装,主要用于处理文件和数据流。Blob 的内容不可修改,并且它适合传输和保存数据(如下载或上传文件)。Blob 可以直接用于显示图像、音频、视频等数据,或者上传文件。
数据操作方式的区别:
ArrayBuffer:需要通过TypedArray(如Uint8Array、Int32Array)或DataView来读写数据。TypedArray可以直接访问数组元素,而DataView提供更灵活的字节级访问能力。Blob:不支持直接访问或操作数据,只能通过FileReader将其读取为文本、Data URL、ArrayBuffer 等格式。
Blob 与 ArrayBuffer 之间的转换:
ArrayBuffer 转 Blob:可以通过 Blob 构造函数将 ArrayBuffer 转换为 Blob:
const buffer = new ArrayBuffer(8);
const blob = new Blob([buffer], { type: 'application/octet-stream' });
备注:
application/octet-stream是一种 MIME 类型,用于表示任意的二进制数据。通常在以下场景中使用:
- 不确定文件类型时的默认类型:当服务器或客户端无法确定文件的具体类型时,会将其标记为
application/octet-stream。它充当一种“通用”二进制格式。- 文件下载:浏览器在接收到
application/octet-stream类型的文件时,通常会提示用户下载,而不是直接显示。因为它表示的内容是原始的二进制数据,可能不是图像、文本或其他常见格式,无法直接展示。- 数据传输的二进制流:如果 Web 应用程序需要以二进制形式传输文件或数据块,可以使用
application/octet-stream来定义内容类型。特别是在处理自定义文件格式或不常见的文件时。示例:
在 HTTP 响应中,设置
application/octet-stream来提示下载:Content-Type: application/octet-stream Content-Disposition: attachment; filename="example.bin"这种方式会通知浏览器这是一个需要下载的二进制文件,不会尝试直接打开它。
Blob 转 ArrayBuffer:可以使用 FileReader 将 Blob 内容读取为 ArrayBuffer:
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result;
};
reader.readAsArrayBuffer(blob);
TypedArray
官方文档:developer.mozilla.org/en-US/docs/…
TypedArray 适合低级别的字节操作,例如解码网络数据包、处理自定义文件格式等。利用 DataView 可以精确地在 ArrayBuffer 上按不同字节顺序或位数读写数据,为字节级操作提供更大的灵活性。
常见的 TypedArray 类型
Int8Array:8位有符号整数Uint8Array:8位无符号整数Int16Array:16位有符号整数Uint16Array:16位无符号整数Int32Array:32位有符号整数Uint32Array:32位无符号整数Float32Array:32位浮点数Float64Array:64位浮点数
JavaScript 中的普通数组不支持直接存储和操作二进制数据,TypedArray 提供了对不同类型(如整数、浮点数等)的精确控制,可以根据需求选择对应的类型,例如 Uint8Array、Int16Array 或 Float32Array。这种控制尤其适合需要进行底层数据处理的场景,比如音频、图像数据处理和计算密集型任务。
许多 Web API(如 WebGL、Web Audio API、WebSockets)都要求使用 TypedArray 来传递数据。TypedArray 提供了与底层 API 之间的桥梁,使数据可以高效、准确地传输。
-
WebGL:在图形编程中,
TypedArray用于传递顶点数据、颜色、纹理坐标等,通常用Float32Array来传递浮点数坐标或用Uint16Array传递索引。const vertices = new Float32Array([ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, ]); -
Web Audio API:用于音频处理,通过
Float32Array来传递音频缓冲数据。
DataView
官方文档:developer.mozilla.org/en-US/docs/…
DataView 是 JavaScript 中一个用来操作和读取二进制数据的对象,特别是在处理 ArrayBuffer 类型的数据时非常有用。它允许你在不需要知道底层数据结构的情况下读取、写入不同类型的数据(如 int8, float32 等),这使得它非常适合用于处理二进制流或与网络协议打交道时。
let buffer = new ArrayBuffer(8); // 创建一个 8 字节的缓冲区
let view = new DataView(buffer);
// 写入数据
view.setInt8(0, 42); // 在偏移量 0 处写入一个 8 位带符号整数
view.setInt32(1, 12345); // 在偏移量 1 处写入一个 32 位带符号整数
// 读取数据
console.log(view.getInt8(0)); // 输出 42
console.log(view.getInt32(1)); // 输出 12345
File
官方文档:developer.mozilla.org/en-US/docs/…
Blob 和 File 类似,但 File 对象专门用于表示用户在 <input type="file"> 中选择的文件。File 继承自 Blob,因此它有相同的属性和方法。
可以使用 new File() 创建一个 File 对象,传入 Blob 数据和文件名:
const file = new File([blob], "example.txt", { type: "text/plain" });
FileReader
官方文档:developer.mozilla.org/zh-CN/docs/…
读取 Blob 或者 File 对象的内容通常使用 FileReader,可以将 Blob 转换为文本、Data URL 或 ArrayBuffer 格式。
const reader = new FileReader();
reader.readAsText(blob); // 读取为文本
reader.onload = () => {
console.log(reader.result); // 输出 Blob 的文本内容
};
FileReader 支持以下几种读取方法:
readAsText(blob): 读取 Blob 并返回文本。
const reader = new FileReader();
reader.onload = function(event) {
console.log(event.target.result); // 文件的文本内容
};
reader.onerror = function(event) {
console.error("文件读取失败", event);
};
reader.readAsText(file);
readAsDataURL(blob): 读取 Blob 并返回 Base64 编码的数据 URL。
const reader = new FileReader();
reader.onload = function(event) {
const dataURL = event.target.result;
document.querySelector('img').src = dataURL; // 预览图片
};
reader.readAsDataURL(file);
readAsArrayBuffer(blob): 读取 Blob 并返回 ArrayBuffer。
const reader = new FileReader();
reader.onload = function(event) {
const arrayBuffer = event.target.result;
console.log(arrayBuffer);
};
reader.readAsArrayBuffer(file);
Base64
官方文档:developer.mozilla.org/en-US/docs/…
Base64 是一种用于将二进制数据编码为 ASCII 字符串的编码方式。它特别适用于需要通过文本传输的二进制数据,例如在 URL、电子邮件和 JSON 中传递图像、文档等数据。Base64 编码的核心思想是将每三个字节的二进制数据转换成四个可打印的 ASCII 字符。其编码过程如下:
- 将数据分割为字节组:将二进制数据分为每 3 个字节(24 位)为一组。
- 转换成字符:将 24 位分为 4 组,每组 6 位,每组使用一个 Base64 表中的字符表示。
- 填充:如果数据长度不是 3 的倍数,就用 0 进行填充,并在编码结果的末尾添加
=符号来补足长度(最多可有两个=)。
基础用法:
- Base64 和字符串的相互转换
注意:btoa 和 atob 仅适用于 ASCII 字符。如果包含非 ASCII 字符(如中文字符),需要对字符串进行编码(如 UTF-8 编码)后再进行 Base64 转换。
// 将字符串编码为 Base64
const encodeToBase64 = (input: string): string => {
return btoa(input);
};
// 将 Base64 解码为字符串
const decodeFromBase64 = (base64: string): string => {
return atob(base64);
};
// 示例
const original = "Hello, TypeScript!";
const base64Encoded = encodeToBase64(original);
console.log(base64Encoded); // "SGVsbG8sIFR5cGVTY3JpcHQh"
console.log(decodeFromBase64(base64Encoded)); // "Hello, TypeScript!"
- Base64 和 Blob 的相互转换
将 Blob(如文件数据)转换为 Base64,或将 Base64 解码为 Blob,通常使用 FileReader API 来处理。
// 将 Blob 转换为 Base64
const blobToBase64 = (blob: Blob): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
const base64 = reader.result?.toString().split(",")[1]; // 去掉 data 前缀
base64 ? resolve(base64) : reject("Failed to convert blob to base64");
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
};
// 将 Base64 转换为 Blob
const base64ToBlob = (base64: string, mimeType: string): Blob => {
const byteString = atob(base64);
const byteNumbers = new Array(byteString.length).map((_, i) =>
byteString.charCodeAt(i)
);
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], { type: mimeType });
};
// 示例
const exampleBase64 = "SGVsbG8sIFR5cGVTY3JpcHQh";
const exampleBlob = base64ToBlob(exampleBase64, "text/plain");
blobToBase64(exampleBlob).then(console.log); // "SGVsbG8sIFR5cGVTY3JpcHQh"
- Base64 和 ArrayBuffer 的相互转换
将 ArrayBuffer 转换为 Base64 主要通过将二进制数据处理为字符串,再进行编码。同理,Base64 可以解码为 ArrayBuffer。
// 将 ArrayBuffer 转换为 Base64
const arrayBufferToBase64 = (buffer: ArrayBuffer): string => {
const bytes = new Uint8Array(buffer);
let binary = "";
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
};
// 将 Base64 转换为 ArrayBuffer
const base64ToArrayBuffer = (base64: string): ArrayBuffer => {
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
};
// 示例
const buffer = new TextEncoder().encode("Hello, ArrayBuffer!").buffer;
const base64EncodedBuffer = arrayBufferToBase64(buffer);
console.log(base64EncodedBuffer); // Base64 string of ArrayBuffer
console.log(new TextDecoder().decode(base64ToArrayBuffer(base64EncodedBuffer))); // "Hello, ArrayBuffer!"
实践
1. 创建一个文件的下载链接
const downloadFile = (content: string | Blob, fileName: string, mimeType: string): void => {
// 将内容包装为 Blob
const blob = content instanceof Blob ? content : new Blob([content], { type: mimeType });
// 创建 URL
const url = URL.createObjectURL(blob);
// 创建下载链接
const link = document.createElement("a");
link.href = url;
link.download = fileName;
// 触发点击事件自动下载
link.click();
// 清理 URL 对象
URL.revokeObjectURL(url);
};
// 示例 - 下载文本文件
downloadFile("Hello, TypeScript!", "example.txt", "text/plain");
2. 加解密操作
在前端的加密和解密应用中,ArrayBuffer 常用于存储和操作密钥、密文等原始二进制数据。许多现代浏览器支持 Crypto API,它允许开发者对 ArrayBuffer 类型的数据进行加密、解密、签名和验证。
const data = new TextEncoder().encode('secret data');
crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: new Uint8Array(12) },
encryptionKey,
data
).then((encryptedData: ArrayBuffer) => {
// 加密后的数据是 ArrayBuffer
});
3. 音频处理和实时音频分析
在 Web Audio API 中,可以使用 ArrayBuffer 来处理和分析音频数据。例如,下载音频文件为 ArrayBuffer 后可以传递到 AudioContext 中进一步处理。这在音频应用和游戏开发中非常有用。
const audioContext = new AudioContext();
fetch('https://example.com/audio.mp3')
.then(response => response.arrayBuffer())
.then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer))
.then(audioBuffer => {
// 可以在 AudioContext 中播放和分析音频数据
});