FORMA

缓冲区

Node.js 缓冲区(Buffer)详解

Buffer 是 Node.js 全局对象,用于处理二进制数据流(如文件、网络数据)。在 ES6 引入 TypedArray 之前,Buffer 是 Node.js 处理二进制的主要方式;现在 Buffer 实现了 Uint8Array 接口,并与 TypedArray 紧密集成。

一、Buffer 与 Uint8Array 的关系

  • 继承关系BufferUint8Array 的子类。因此,Buffer 实例具有 Uint8Array 的所有方法(如 forEachmap 等),并且可以使用 Uint8Array 的静态方法。
  • 内存分配Buffer 使用 Node.js 底层的 libuv 内存分配策略,而普通 Uint8Array 由 V8 分配。Buffer 在某些情况下可以申请未初始化的内存allocUnsafe),性能更高但存在泄露旧数据的风险。
  • 适用场景
    • Buffer:专为 Node.js I/O 设计,提供更方便的字符串与二进制转换(如 toString 支持多种编码)、concatcompare 等方法。
    • Uint8Array:通用二进制数组,可用于浏览器环境或更底层的数值操作。

相互转换

js
const buf = Buffer.from("hello");
const uint8 = new Uint8Array(buf); // 共享同一段内存
const buf2 = Buffer.from(uint8); // 复制数据

注意Buffer 实例的 length 是其字节长度,并非元素个数(与 Uint8Array 一致)。

二、创建 Buffer 的方式

1. Buffer.alloc(size[, fill[, encoding]])

  • 作用:创建一个指定大小的 Buffer,内存会被初始化(默认填充 0),安全且性能可接受。
  • 推荐:始终优先使用 alloc 确保数据安全,避免泄漏敏感信息。
js
const buf1 = Buffer.alloc(10); // 10 字节,全 0
const buf2 = Buffer.alloc(10, 0x2a); // 填充为 0x2a ('*')
const buf3 = Buffer.alloc(10, "a", "utf8"); // 填充字符 'a'

2. Buffer.from(array)

  • 作用:从数组、字符串或其他 Buffer 创建新 Buffer。支持多种输入类型:
    • Buffer.from(string[, encoding]):从字符串创建。
    • Buffer.from(buffer):复制现有 Buffer 数据。
    • Buffer.from(array):从字节数组(0-255 整数数组)创建。
js
const buf1 = Buffer.from("hello", "utf8");
const buf2 = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]); // 等价
const buf3 = Buffer.from(buf1); // 新副本

3. Buffer.allocUnsafe(size) / allocUnsafeSlow(size)

  • 作用:分配未初始化的内存,速度快,但可能包含旧数据(敏感信息风险)。需立即写入数据,否则可能泄露。
  • 使用场景:性能极端敏感且能确保立即覆写全部字节时。
  • 风险:未初始化的内存片段可能包含密码、密钥等,必须小心使用。
js
const buf = Buffer.allocUnsafe(1024);
buf.fill(0); // 立即填充以避免泄露

对比

方法初始化性能安全性推荐程度
alloc是(填 0)中等默认首选
from取决于源中等从现有数据创建
allocUnsafe最快低(需谨慎)仅特殊优化场景

三、常用方法

1. buf.toString([encoding[, start[, end]]])

将 Buffer 解码为字符串,默认 utf8 编码。可指定起始/结束位置。

js
const buf = Buffer.from("Node.js 缓冲区");
console.log(buf.toString("utf8", 0, 5)); // "Node."
console.log(buf.toString("hex")); // 十六进制表示

2. Buffer.concat(list[, totalLength])

将多个 Buffer 拼接成一个新 Buffer,高效合并。

js
const bufs = [Buffer.from("Hello"), Buffer.from(" "), Buffer.from("World")];
const result = Buffer.concat(bufs); // <Buffer 48 65 6c 6c 6f 20 57 6f 72 6c 64>
console.log(result.toString()); // "Hello World"

若已知总长度,可传入 totalLength 优化性能。

3. buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])

比较两个 Buffer 的字节顺序,常用于排序:

  • 返回 0 相等,< 0 表示 buftarget 之前,> 0 在后面。
js
const a = Buffer.from("abc");
const b = Buffer.from("abd");
console.log(a.compare(b)); // -1 (因为 'c' < 'd')

4. buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

将 Buffer 中的数据复制到另一个 Buffer,不创建新 Buffer。

js
const src = Buffer.from("Hello World");
const dst = Buffer.alloc(5);
src.copy(dst, 0, 0, 5);
console.log(dst.toString()); // "Hello"

5. buf.slice([start[, end]])

返回指向同一段内存的新 Buffer(浅拷贝)。注意:修改切片会影响原 Buffer。

js
const original = Buffer.from("abcdefg");
const sliced = original.slice(1, 4); // bcd
sliced[0] = 0x7a; // 修改切片会影响 original
console.log(original); // <Buffer 61 7a 63 64 65 66 67> (a z c d e f g)

若需独立副本,可对切片调用 Buffer.from(sliced)

6. 其他常用方法

  • buf.length:Buffer 的字节数(注意不是字符数,例如中文字符占 3 字节)。
  • buf.fill(value[, offset[, end]]):填充 Buffer。
  • buf.indexOf(value[, byteOffset]):查找指定内容的位置。
  • buf.equals(otherBuffer):判断两个 Buffer 内容是否完全相同。

四、性能与安全注意事项

  1. 优先使用 Buffer.allocBuffer.from,避免 allocUnsafe 除非你确切知道风险并立即覆写。
  2. 不要使用 new Buffer(size),该构造函数已废弃,行为不可预测。
  3. 处理大文件或网络数据时,使用 Buffer 配合流(stream)和 StringDecoder 处理多字节字符(如 UTF-8 中文字符可能被分割)。
  4. Uint8Array 互操作时,注意 BufferUint8Array 的子类,但有些 TypedArray 方法可能未在 Buffer 上重写(如 setsubarray),建议转换后使用。

总结表

创建方式安全性能适用场景
Buffer.alloc日常使用,默认选择
Buffer.from从字符串/数组/现有 Buffer 创建
Buffer.allocUnsafe对性能极致要求且能立即填充的场景
方法功能是否修改原 Buffer
toString解码为字符串
concat合并多个 Buffer否(返回新 Buffer)
compare比较字节顺序
copy复制数据到目标是(修改目标 Buffer)
slice创建共享内存视图否(但修改视图会影响原 Buffer)

学习建议:结合 Node.js 文档和实际项目(如文件上传、socket 数据处理)来理解 Buffer 的使用,同时注意字符编码问题(特别是 UTF-8 的多字节字符)。

推荐参考资料

  • Node.js 官方文档:Buffer
  • 《深入浅出 Node.js》相关章节
  • 线上工具:使用 hex 编码查看 Buffer 原始字节

掌握 Buffer 是编写高性能 Node.js 网络与文件应用的基础。

Series

core

1 / 7

文件系统