在现代前端开发中,处理二进制数据变得越来越重要。从图像、音频到文件上传,这些数据类型常常以二进制形式存在。这个分享将带你深入探索 ArrayBuffer、Blob、File 以及流(Stream)等概念,探讨它们如何在前端开发中发挥作用,解锁了解和利用二进制数据的强大能力。
传统上,前端开发主要关注文本和结构化数据,但随着应用程序的复杂性增加,处理图像、音频和大型文件等二进制数据变得至关重要。这种数据的处理需要特定的工具和技术,而 ArrayBuffer、Blob、File 以及流(Stream)就提供了这样的能力。
ArrayBuffer 是 JavaScript 中用于表示一段固定长度的原始二进制数据的对象。它提供了一个固定大小的缓冲区,但不能直接读取或写入数据。相反,你需要使用 TypedArrays 或 DataView 对其进行操作和访问。
Blob 和 File 是用于表示二进制数据的对象。Blob(Binary Large Object)用于存储不可变的原始数据,而 File 对象是 Blob 的一种特殊形式,它包含了与文件相关的额外元数据,比如文件名、文件大小等信息。Blob 和 File 通常用于处理文件、图像、音频等二进制数据。
流是一种处理大量数据或实时数据的方式。在前端开发中,流提供了一种以逐段方式读取和处理数据的机制,避免一次性加载整个数据集到内存中。流在处理网络数据、大文件或实时数据时非常有用,它允许逐步处理数据,减少内存占用并提高性能。
TypedArrays 是 JavaScript 中一系列特定类型的数组,比如 Uint8Array、Float32Array 等。它们基于 ArrayBuffer,允许以不同的数据类型(如整数或浮点数)操作二进制数据,提供了更高效的二进制数据处理方式。
Blob
是一种表示不可变的原始二进制数据的对象,它可以包含任意类型的数据。File
对象继承自 Blob
,因此 File
也具有 Blob
的所有特性。2. File 是 Blob 的拓展:
File
是 Blob
的拓展,它包含了一些额外的元数据,如文件名、最后修改时间、文件大小等。这使得 File
更适合用于表示用户通过文件输入字段选择的文件。3. 创建方式:
你可以使用 Blob
构造函数创建一个通用的 Blob 对象,而使用 File
构造函数创建一个表示文件的 File
对象。但需要注意,File
构造函数除了接受数据和文件名外,还可以接受一些可选的参数,如最后修改时间等。
var a Blob = new Blob( array, options );
ArrayBuffer
, ArrayBufferView
, Blob
, DOMString
等对象构成的 Array
,或者其他类似对象的混合体,它将会被放进 Blob
。DOMStrings 会被编码为 UTF-8。BlobPropertyBag
字典,它可能会指定如下两个属性:
type
,默认值为 ""
,它代表了将会被放入到 blob 中的数组内容的 MIME 类型。endings
,默认值为"transparent"
,用于指定包含行结束符\n
的字符串如何被写入。它是以下两个值中的一个:"native"
,代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent"
,代表会保持 blob 中保存的结束符不变 非标准// 创建一个通用的 Blob 对象
const blob = new Blob(['Hello, Blob!'], { type: 'text/plain' });
// 创建一个表示文件的 File 对象
const file = new File(['Hello, File!'], 'example.txt', { type: 'text/plain', lastModified: Date.now() });
File()
构造器创建新的 File
对象实例。
new File(bits, name)
new File(bits, name, options)
var file = new File(["foo"], "foo.txt", {type: "text/plain",});
4. 用途 :
Blob
主要用于表示任意类型的二进制数据,而 File
更适合用于表示文件,特别是在涉及到用户上传文件时。// 使用 Blob 创建 URL 并显示在页面上
const blobUrl = URL.createObjectURL(blob);
// 使用 File 创建 URL 并显示在页面上
const fileUrl = URL.createObjectURL(file);
总体来说,File
可以看作是 Blob
的子集,它在 Blob
的基础上添加了文件相关的元数据,使得它更适合处理文件的场景。在很多情况下,你可以使用 Blob
来表示一般的二进制数据,而在处理用户上传的文件时,使用 File
会更为合适。
当涉及到JavaScript中的二进制数据处理时,ArrayBuffer
、DataView
和 Typed Arrays 是密切相关的。
他们的关系如下图所示,TypedArray和DataView都是Arraybuffer的视图,可以通过这两个数据类型去操作原始的二进制数据。
ArrayBuffer
是一种通用的、固定长度的二进制数据缓冲区。它表示一段连续的、通常是固定大小的内存区域,但是不能直接进行读写操作。它是一种容器,用于存储二进制数据,但没有提供直接的方法来操作这些数据。Int8Array
, Uint8Array
, Int16Array
, Uint16Array
, Int32Array
, Uint32Array
, Float32Array
, Float64Array
等等,每个都对应着不同的二进制数据格式。DataView
是一个允许你以不同的数据格式(例如整数、浮点数等)从 ArrayBuffer 中读取和写入数据的机制。它提供了一种灵活的方式来解释 ArrayBuffer 中的数据,因此你可以根据需要以不同的格式读取或写入数据。关系可以总结如下:
ArrayBuffer
提供了存储二进制数据的固定大小缓冲区。DataView
允许以不同的数据格式查看和修改 ArrayBuffer 中的数据,不同于 Typed Arrays,它可以手动指定数据的类型和字节偏移量。当使用 ArrayBuffer
、TypedArray
和 DataView
时,以下是一些简单的示例代码:
// 创建一个包含 8 个字节的 ArrayBuffer
const buffer = new ArrayBuffer(8);
// 获取 ArrayBuffer 的字节长度
const byteLength = buffer.byteLength;
// 输出字节长度
console.log(`Byte Length: ${byteLength}`);
// 使用 Int32Array 来操作 ArrayBuffer
const buffer = new ArrayBuffer(8);
const int32Array = new Int32Array(buffer);
// 设置第一个元素的值
int32Array[0] = 42;
// 获取第二个元素的值(注意,这里使用了 Int32Array,所以一个元素占据 4 个字节)
const secondValue = int32Array[1];
console.log(`First Element: ${int32Array[0]}`);
console.log(`Second Element: ${secondValue}`);
// 创建一个包含 8 个字节的 ArrayBuffer
const buffer = new ArrayBuffer(8);
// 使用 DataView 来操作 ArrayBuffer
const dataView = new DataView(buffer);
// 设置第一个 32 位整数(字节偏移量为 0)
dataView.setInt32(0, 42);
// 获取第二个 32 位整数(字节偏移量为 4)
const secondValue = dataView.getInt32(4);
console.log(`First Element: ${dataView.getInt32(0)}`);
console.log(`Second Element: ${secondValue}`);
这些示例演示了如何创建 ArrayBuffer
,使用 TypedArray
来访问和修改其数据,以及如何使用 DataView
进行更灵活的操作。请注意,TypedArray
和 DataView
是 ArrayBuffer 的视图,它们提供了不同的方法来处理底层数据。
当涉及到数据的处理和转换时,Blob、File 和 ArrayBuffer 提供了一些方法来读取、写入和转换数据。
readAsText()
、readAsArrayBuffer()
、readAsDataURL()
等。const file = /* 获取的 File 对象 */;
const reader = new FileReader();
reader.onload = function(event) {const result = event.target.result;// 使用读取的数据进行操作
};
reader.readAsText(file); // 以文本格式读取文件内容
const buffer = /* 获取的 ArrayBuffer 对象 */;
const view = new DataView(buffer);
const value = view.getInt16(0); // 从偏移量为 0 的位置读取一个整数值
readAsArrayBuffer()
方法,将 Blob 或 File 对象中的数据转换为 ArrayBuffer。const blob = /* 获取的 Blob 对象 */;
const reader = new FileReader();
reader.onload = function(event) {const arrayBuffer = event.target.result;// 使用转换后的 ArrayBuffer 进行操作
};
reader.readAsArrayBuffer(blob); // 将 Blob 转换为 ArrayBuffer
const arrayBuffer = /* 获取的 ArrayBuffer 对象 */;
const blob = new Blob([arrayBuffer], { type: 'application/octet-stream' });
这些方法可以帮助你从 Blob、File 和 ArrayBuffer 中读取和写入数据,以及在不同格式间进行转换。这在处理文件、图像、音频或视频等二进制数据时非常有用。
暂时无法在飞书文档外展示此内容
在前端开发中,Stream API 是一组用于处理数据流的功能接口,用于处理输入和输出流的数据。HTML5 中引入了一些与流相关的 API,让开发者可以更方便地操作数据流。
1. ReadableStream(可读流): `ReadableStream` 表示一种可读取的数据流。它允许开发者从各种数据源(例如网络请求、文件、Blob 对象等)读取数据。
// 创建一个可读流
const stream = new ReadableStream({
start(controller) {
// 从某个数据源生成数据并通过 controller.enqueue() 发送
},
pull(controller) {
// 当需要更多数据时调用,可以在这里继续发送数据
},
cancel(reason) {
// 在流被取消时执行的操作
}
});
```
2. WritableStream(可写流): `WritableStream` 用于写入数据流。它提供了一个接口,可以将数据写入到目标中。
// 创建一个可写流
const writableStream = new WritableStream({
write(chunk) {
// 将数据写入到目标中
},
close() {
// 在流关闭时执行的操作
},
abort(reason) {
// 在流被中止时执行的操作
}
});
```
3. TransformStream(转换流): `TransformStream` 是一个通过对输入流进行处理生成输出流的接口,允许对数据进行转换、过滤或修改等操作。
// 创建一个转换流
const transformStream = new TransformStream({
start(controller) {
// 初始化转换流
},
transform(chunk, controller) {
// 对输入数据进行转换处理,并通过 controller.enqueue() 发送输出数据
},
flush(controller) {
// 在数据处理完成后执行的操作
}
});
```
这些 API 提供了一种灵活的方式来处理数据流,可以用于各种场景,例如从网络请求中读取数据、处理文件流、实现流式传输等。在实际应用中,开发者可以利用这些 API 进行数据处理、转换、过滤等操作,从而更高效地处理大量数据而无需等待整个数据加载完成。
浏览器中直接内置了许多有用的视频流。您可以轻松地从 blob 创建 ReadableStream
。Blob
接口的 stream() 方法会返回一个 ReadableStream
,在读取时,该方法会返回 blob 中包含的数据。另外请注意,File
对象是 Blob
的特定类型,可以在 blob 支持的任何上下文中使用。
const readableStream = new Blob(['hello world'], { type: 'text/plain' }).stream();
TextDecoder.decode()
和 TextEncoder.encode()
的流式传输变体分别称为 TextDecoderStream
和 TextEncoderStream
。
const response = await fetch('https://streams.spec.whatwg.org/');
const decodedStream = response.body.pipeThrough(new TextDecoderStream());
使用分别使用 CompressionStream
和 DecompressionStream
转换流可以轻松压缩或解压缩文件。以下代码示例展示了如何下载 Streams 规范,直接在浏览器中对其进行压缩 (gzip),以及将压缩后的文件直接写入磁盘。
const response = await fetch('https://streams.spec.whatwg.org/');
const readableStream = response.body;
const compressedStream = readableStream.pipeThrough(new CompressionStream('gzip'));
const fileHandle = await showSaveFilePicker();
const writableStream = await fileHandle.createWritable();
compressedStream.pipeTo(writableStream);
例如,File System Access API 的 FileSystemWritableFileStream
和实验性 fetch() 请求流
就是可写流的示例。
Serial API 大量使用了可读流和可写数据流。
// Prompt user to select any serial port.
const port = await navigator.serial.requestPort();
// Wait for the serial port to open.
await port.open({ baudRate: 9_600 });
const reader = port.readable.getReader();
// Listen to data coming from the serial device.
while (true) {
const { value, done } = await reader.read();
if (done) {
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
// value is a Uint8Array.
console.log(value);
}
// Write to the serial port.
const writer = port.writable.getWriter();
const data = new Uint8Array([104, 101, 108, 108, 111]); // hello
await writer.write(data);
// Allow the serial port to be closed later.
writer.releaseLock();
最后,WebSocketStream
API 将数据流与 WebSocket API 集成。
const wss = new WebSocketStream(WSS_URL);
const { readable, writable } = await wss.connection;
const reader = readable.getReader();
const writer = writable.getWriter();
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
const result = await process(value);
await writer.write(result);
}
stream()
方法创建一个 ReadableStream。下面是一个使用 Mermaid 绘制的流程图,展示了这些流相关的 API 和功能之间的关系:
这个关系图用于展示这些流相关 API 之间的关联,同时也指明了它们之间的一些交互和用途。
暂时无法在飞书文档外展示此内容
取得 Blob
对象以后,可以通过 FileReader
对象,读取 Blob
对象的内容,即文件内容。
FileReader
对象提供四个方法。将 Blob 对象作为参数传入,然后以指定的格式返回。
FileReader.readAsText()
:返回文本,需要指定文本编码,默认为 UTF-8。FileReader.readAsArrayBuffer()
:返回 ArrayBuffer 对象。FileReader.readAsDataURL()
:返回 Data URL。FileReader.readAsBinaryString()
:返回原始的二进制字符串。下面是 FileReader.readAsText()
方法的例子,用来读取文本文件:
// HTML 代码如下
// <input type='file' onchange='readfile(this.files[0])'></input>
// <pre id='output'></pre>
function readfile(f) {
let reader = new FileReader();
reader.readAsText(f);
reader.onload = function () {
let text = reader.result;
let out = document.getElementById('output');
out.innerHTML = '';
out.appendChild(document.createTextNode(text));
}
reader.onerror = function(e) {
console.log('Error', e);
};
}
下面是 FileReader.readAsArrayBuffer()
方法的例子,用于读取二进制文件:
// HTML 代码如下
// <input type="file" onchange="typefile(this.files[0])"></input>
function typefile(file) {
// 文件开头的四个字节,生成一个 Blob 对象
let slice = file.slice(0, 4);
let reader = new FileReader();
// 读取这四个字节
reader.readAsArrayBuffer(slice);
reader.onload = function (e) {
let buffer = reader.result;
// 将这四个字节的内容,视作一个32位整数
let view = new DataView(buffer);
let magic = view.getUint32(0, false);
// 根据文件的前四个字节,判断它的类型
switch(magic) {
case 0x89504E47: file.verified_type = 'image/png'; break;
case 0x47494638: file.verified_type = 'image/gif'; break;
case 0x25504446: file.verified_type = 'application/pdf'; break;
case 0x504b0304: file.verified_type = 'application/zip'; break;
}
console.log(file.name, file.verified_type);
};
}
Uint8ClampedArray
操作图像 Uint8ClampedArray
是一个存储 8 位无符号整数的 JavaScript 数组,常用于处理图像数据。它保证数值在 0 到 255 范围内,特别适用于图像处理,因为它确保像素值有效且性能高效。
// 获取图像像素数据
const imageData = tempCtx.getImageData(0, 0, img.width, img.height);
const data = imageData.data;
// 转换为灰度图像
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // Red
data[i + 1] = avg; // Green
data[i + 2] = avg; // Blue
}
// 将修改后的像素数据放回临时 canvas
tempCtx.putImageData(imageData, 0, 0);
调用 getImageData
方法时,它返回一个包含图像数据的 ImageData
对象。ImageData
对象中的 data
属性是一个 Uint8ClampedArray
,它包含了图像的像素数据。
在这个例子中,我们首先获取了原始图像的像素数据 data
。这个数据是一个 Uint8ClampedArray
,每个像素占据四个连续的位置(分别是红、绿、蓝和 alpha 通道,每个通道占一个字节)。所以 data
中的长度是图像宽度乘以图像高度乘以 4。
接着,我们对每个像素进行灰度化处理,将 RGB 三个通道的值取平均值,这样就得到了灰度值。在 Uint8ClampedArray
中,data[i]
是红色通道的值,data[i + 1]
是绿色通道的值,data[i + 2]
是蓝色通道的值,data[i + 3]
是 alpha 通道的值。因此,通过将 RGB 三个通道的值设置为相同的灰度值,我们将图像转换为灰度图像。
最后,我们通过 putImageData
方法将修改后的像素数据重新放回了 tempCtx
,这样就在临时 canvas 上得到了灰度图像。
暂时无法在飞书文档外展示此内容
application/octet-stream
类型的文件当后端返回 application/octet-stream
类型的数据时,通常表示这是一种通用的二进制数据类型,可能是文件或其他二进制数据,比如图片、音频、视频等。前端可以通过以下方式处理并下载这种类型的数据:
使用 Fetch API 下载数据:
fetch('your_endpoint_url')
.then(response => response.blob())
.then(blobData => {
// 创建一个 URL 对象
const blobUrl = URL.createObjectURL(blobData);
// 创建一个 <a> 元素
const link = document.createElement('a');
link.href = blobUrl;
link.download = 'file_name'; // 下载文件时保存的文件名// 将 <a> 元素添加到页面中并触发下载document.body.appendChild(link);
link.click();
// 下载完成后移除 <a> 元素和 URL 对象
document.body.removeChild(link);
URL.revokeObjectURL(blobUrl);
})
.catch(error => {console.error('下载出错:', error);
});
这段代码通过 Fetch API 获取数据,然后将其转换为 Blob
对象。接着,利用 URL.createObjectURL()
创建了一个 URL,并将其赋给 <a>
元素的 href
属性。设置 download
属性可以指定下载文件时的文件名。最后,通过模拟点击 <a>
元素来触发下载。
binaryType
binaryType
是 WebSocket 对象的属性,它用于指定在通过 WebSocket 接收或发送二进制数据时的处理方式。这个属性有两个可能的值:
设置 binaryType
可以影响通过 WebSocket 发送和接收数据的方式。如果您期望接收到的数据是 Blob 对象,可以将 binaryType
设置为 "blob";如果希望数据以 ArrayBuffer 对象的形式接收,则将其设置为 "arraybuffer"。
例如,在 JavaScript 中,您可以这样设置 binaryType
属性:
// 创建 WebSocket 连接
const socket = new WebSocket('ws://example.com');
// 设置 binaryType
socket.binaryType = 'arraybuffer'; // 或者 'blob'
这对于处理 WebSocket 连接中的二进制数据非常有用,可以根据需要选择适合的数据类型来接收和处理数据。
一些常见的 MIME 类型可以用于表示不同类型的数据:
'image/jpeg'
:表示 JPEG 图像文件。'image/png'
:表示 PNG 图像文件。'application/pdf'
:表示 PDF 文档。'audio/mpeg'
:表示 MP3 音频文件。'video/mp4'
:表示 MP4 视频文件。'application/json'
:表示 JSON 数据。'application/xml'
:表示 XML 数据。'application/octet-stream'
: 表示二进制数据原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。