ArrayBuffer / TypedArray / Blob / File 关系与操作指南

13 阅读3分钟

一、核心概念与关系

ArrayBuffer  ──────────────────────────────────────────────────
   │  原始二进制内存,不能直接读写                                  │
   ▼                                                             │
TypedArray / DataView  ← 视图层,用于读写 ArrayBuffer            │
(Uint8Array, Int32Array, Float64Array…)                         │
                                                                 │
Blob  ───── 不可变的二进制大对象,有 type(MIME)                  │
   │                                                             │
   ▼ (继承)                                                      │
File  ───── 在 Blob 基础上增加 name / lastModified               │
                                                                 │
互转链路:                                                       │
ArrayBuffer  ──►  Uint8Array  ──►  Blob  ──►  FileFile / Blob  ──►  ArrayBuffer  (通过 .arrayBuffer() 或 FileReader)

二、各类型速查

类型可变?直接读写?用途
ArrayBuffer否(需视图)原始内存容器
TypedArray数值型数组操作
DataView精确控制字节序(endian)
Blob文件内容、网络传输
File用户上传文件

三、ArrayBuffer 操作

3.1 创建

const buf = new ArrayBuffer(16); // 分配 16 字节,全为 0

3.2 通过 TypedArray 读写

const view = new Uint8Array(buf);
view[0] = 255;
view[1] = 128;

const int32 = new Int32Array(buf);
int32[0] = 42; // 占 4 字节

const float64 = new Float64Array(buf);
float64[0] = 3.14; // 占 8 字节

3.3 通过 DataView 控制字节序

const dv = new DataView(buf);

dv.setInt32(0, 300, true);   // true = 小端(little-endian)
dv.getInt32(0, true);        // 读取 → 300

dv.setFloat32(4, 1.5, false); // false = 大端(big-endian)
dv.getFloat32(4, false);      // → 1.5

3.4 切片(复制新 buffer)

const sliced = buf.slice(0, 8); // 取前 8 字节,返回新 ArrayBuffer

3.5 复制 / 合并

// 合并多个 ArrayBuffer
function concat(...buffers) {
  const total = buffers.reduce((n, b) => n + b.byteLength, 0);
  const result = new Uint8Array(total);
  let offset = 0;
  for (const b of buffers) {
    result.set(new Uint8Array(b), offset);
    offset += b.byteLength;
  }
  return result.buffer;
}

3.6 转字符串

// ArrayBuffer → UTF-8 字符串
const decoder = new TextDecoder('utf-8');
const str = decoder.decode(buf);

// 字符串 → ArrayBuffer
const encoder = new TextEncoder();
const buf2 = encoder.encode('Hello').buffer;

四、互转方法

4.1 ArrayBuffer → Blob

const blob = new Blob([arrayBuffer], { type: 'application/octet-stream' });

4.2 Blob → ArrayBuffer

// 现代方式(推荐)
const buf = await blob.arrayBuffer();

// 旧方式(兼容性)
function blobToBuffer(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsArrayBuffer(blob);
  });
}

4.3 Blob → File

const file = new File([blob], 'example.png', {
  type: 'image/png',
  lastModified: Date.now(),
});

4.4 File → ArrayBuffer

const buf = await file.arrayBuffer(); // File 继承自 Blob

4.5 ArrayBuffer → Base64

const base64 = btoa(
  String.fromCharCode(...new Uint8Array(arrayBuffer))
);

// Base64 → ArrayBuffer
const bin = atob(base64);
const buf = new Uint8Array(bin.length).map((_, i) => bin.charCodeAt(i)).buffer;

4.6 Blob → Object URL(预览/下载)

const url = URL.createObjectURL(blob);
// 用完释放
URL.revokeObjectURL(url);

4.7 Blob → 文本

const text = await blob.text();
const json = JSON.parse(await blob.text());

五、常用场景示例

5.1 读取用户上传文件

input.addEventListener('change', async (e) => {
  const file = e.target.files[0];          // File 对象
  const buffer = await file.arrayBuffer(); // → ArrayBuffer
  const view   = new Uint8Array(buffer);
  console.log(view[0], view[1]); // 读取头两字节
});

5.2 下载二进制数据

const response = await fetch('/api/file');
const buffer = await response.arrayBuffer();
const blob    = new Blob([buffer], { type: 'application/pdf' });
const a       = document.createElement('a');
a.href        = URL.createObjectURL(blob);
a.download    = 'result.pdf';
a.click();
URL.revokeObjectURL(a.href);

5.3 修改图片二进制头(示例:读 PNG 宽高)

const buf = await file.arrayBuffer();
const dv  = new DataView(buf);
// PNG: 宽在偏移 16,高在偏移 20(大端)
const width  = dv.getUint32(16, false);
const height = dv.getUint32(20, false);

5.4 WebSocket 发送 / 接收二进制

ws.binaryType = 'arraybuffer';
ws.onmessage = (e) => {
  const view = new Uint8Array(e.data); // 直接是 ArrayBuffer
};

// 发送
ws.send(new Uint8Array([0x01, 0x02, 0x03]).buffer);

5.5 上传 Blob / File(FormData)

const form = new FormData();
form.append('file', file);             // File 对象
// 或用 Blob + 指定文件名
form.append('file', blob, 'data.bin');

await fetch('/upload', { method: 'POST', body: form });

六、关系总图(文字版)

用户选文件
    │
    ▼
  File  (name, lastModified, type)
    │   继承
    ▼
  Blob  (不可变, type)
    │
    ├─ .arrayBuffer()  ──►  ArrayBuffer  ──►  TypedArray / DataView  (读写字节)
    ├─ .text()         ──►  string
    ├─ .stream()       ──►  ReadableStream
    └─ URL.createObjectURL()  ──►  blob:// URL  ──►  <img src> / <a download>

ArrayBuffer
    ├─ new Blob([buf])           ──►  Blob
    ├─ new Uint8Array(buf)       ──►  TypedArray(视图)
    ├─ new DataView(buf)         ──►  DataView(视图)
    └─ TextDecoder.decode(buf)   ──►  string

七、兼容性提示

API兼容性
blob.arrayBuffer()Chrome 76+, Firefox 69+, Node 15+
blob.text()同上
FileReader全浏览器,旧项目兼容方案
TextEncoder/Decoder现代浏览器 & Node 11+
ArrayBuffer.transfer()Chrome 114+(零拷贝转移所有权)