Buffer
是 Node.js 中用于操作二进制数据的全局类,可以直接使用,无需导入。
它的主要作用是存储和处理二进制数据,类似于一个伪数组。
- 每个
Buffer
实例的元素占用 1 个字节(8 位),并且通常以十六进制形式显示,方便查看和操作。 - 在 Node.js 默认使用的 UTF-8 编码中:
- 英文字母(以及其他 ASCII 字符)占用 1 个字节。
- 汉字等非 ASCII 字符通常占用 3 个字节。
const buffer = new Buffer('hello');
// 字幕 => ASCII 码 => 二进制 => 十六进制
// H => 104 => 01101000 => 68
// e => 101 => 01100101 => 65
// l => 108 => 01101100 => 6C
// l => 108 => 01101100 => 6C
// o => 111 => 01101111 => 6F
console.log(buffer); // <Buffer 68 65 6c 6c 6f>
在旧版本的 Node.js 中,使用 new Buffer()
创建 Buffer 时,会分配一块未初始化的内存。这意味着内存中的旧数据可能会被暴露,存在潜在的安全风险(例如泄露敏感信息)
因此从 Node.js 6.0.0 开始,推荐使用 Buffer.from()
方法来替换new Buffer()
。他们功能完全一致,但为了向后兼容, new Buffer()
依旧是可用的
const buffer = Buffer.from('你好');
// 你 => e4 bd a0 => 11100100 10111101 10100000
// 好 => e5 a5 bd => 11100101 10100101 10111101
console.log(buffer); // <Buffer e4 bd a0 e5 a5 bd>
参数
Buffer.from
可以传递的参数为 数组 或 字符串
const buffer = Buffer.from([1, 2, 3]);
console.log(buffer); // <Buffer 01 02 03>
const buffer2 = Buffer.from('hello');
console.log(buffer2); // <Buffer 68 65 6c 6c 6f>
分配内存
在早期版本的 Node.js(v6.x 及更早版本)中,new Buffer
是创建 Buffer
的主要方式:
new Buffer(size)
:分配一块指定大小的内存,但内容未初始化,可能包含旧数据(如敏感信息),存在信息泄露风险。new Buffer(data)
:根据传入的数据(如字符串、数组等)创建一个Buffer
。
为此 Node.js 对该方法进行了拆分
-
Buffer.alloc(size)
-
分配一块指定大小的内存,并将内容初始化为零。
-
推荐使用,默认安全。
-
示例:
const buffer = Buffer.alloc(10); console.log(buffer); // <Buffer 00 00 00 00 00 00 00 00 00 00>
-
-
Buffer.allocUnsafe(size)
-
分配一块指定大小的内存,但内容未初始化。
-
性能更高,但需要开发者自行清理内存内容,谨慎使用。
-
示例:
const buffer = Buffer.allocUnsafe(10); console.log(buffer); // 输出未初始化的随机内容
-
-
Buffer.from(data)
-
根据传入的数据(如字符串、数组等)创建一个新的
Buffer
。 -
替代
new Buffer(data)
的安全方法。 -
示例:
const buffer = Buffer.from('你好'); console.log(buffer); // <Buffer e4 bd a0 e5 a5 bd>
-
目前依旧可以使用
new Buffer('你好')
,但是无法使用new Buffer(10)
使用
new Buffer(10)
, 控制台会静默失效,并抛出经过⚠️
编码格式
Buffer 默认使用 UTF-8 编码来存储字符串。如果我们希望使用其他编码格式,可以通过 Buffer.from()
的第二个参数来指定
- UTF-8:中文字符通常需要 3 个字节。
- UTF-16:中文字符通常需要 2 个字节。
// utf-8 等价于 utf8
// utf16le 等价于 utf-16le 「 utf-16 的 小端字节序(little endian) 版本 」
const buffer = Buffer.from('你好', 'utf16le');
console.log(buffer); // <Buffer 60 4f 7d 59>
同样new Buffer
也可以通过第二个参数,指定编码格式
const buffer = new Buffer('你好', 'utf-16le');
console.log(buffer); // <Buffer 60 4f 7d 59>
转字符串
我们可以将 Buffer 转换回字符串,使用 toString()
方法
const buffer = Buffer.from('hello');
console.log(buffer.toString()); // hello
通用toString
可以通过参数指定转换的编码格式。在编码和解码过程中,必须保证使用的编码格式一致,以避免乱码问题
const buffer = Buffer.from('你好', 'utf16le');
console.log(buffer.toString('utf16le')); // 正确解码
console.log(buffer.toString('utf8')); // 错误解码(可能出现乱码)
文件读取
import fs from 'fs/promises';
// 读取文件时,默认读取的是二进制数据 <Buffer 实例>
const data = await fs.readFile('./assets/users.json');
console.log(data);
// toString的默认编码是utf-8
console.log(data.toString()); // Buffer => String
// 读取文件时, 指定编码
const data2 = await fs.readFile('./assets/users.json', 'utf-8');
console.log(data2);
内存池
在 Node.js 中,每次创建 Buffer
时,如果直接向操作系统申请内存,会导致频繁的系统调用,从而影响性能。为了优化这种情况,Node.js 引入了内存池机制。
当我们第一次创建一个小型 Buffer
(比如 Buffer.alloc(8)
),虽然只需要 8 字节,但 Node.js 实际上会一次性向操作系统申请一块**8KB(8192 字节)**的内存作为内存池。这块内存池可以被后续的小型 Buffer
重复使用。
- 如果创建的
Buffer
大小小于 8KB,且内存池中有足够的空间,Node.js 会直接从内存池中分配内存,而不需要再次向操作系统申请。 - 如果内存池的剩余空间不足,则会重新申请一块新的 8KB 内存。
- 对于超过 8KB 的大 Buffer(比如 10KB),Node.js 不会使用内存池,而是直接向操作系统申请独立的内存。
内存池机制只适用于小型 Buffer
(小于 8KB)。对于较大的 Buffer
,无论使用 Buffer.alloc()
还是 Buffer.from()
,都会直接向操作系统申请内存。