Node.js Buffers 完整指南

在Node.js中,buffers 是一个可以存储二进制数据的特殊类型。buffer 代表内存块-通常指在计算机中分配的 RAW 。buffer 的大小是不能更改的。

buffer 存储字节。八位(Bits)序列称为一个字节(byte)。位(Bits)在计算机中是最基本的存储单元,他们可以保存0 或 1的数值。

译者注:计算机是存储二进制数据的,二进制主要是 0 和 1的集合。

Node.js 在全局作用域中可直接使用 Buffer 类(不需要像其他模块一样导入)。使用这个API,你可以获取一系列函数和抽象来操作原始的二进制文件。

一个 buffer 在Node.js中就如以下表示:

<Buffer 61 2e 71 3b 65 2e 31 2f 61 2e> 

在示例中,你可以看到 10对字母和数字的组成,每一对表示存储在缓冲区中的字节,这个缓冲区的总大小为10。

你可能会问自己:“如果这些是位和字节,那 0 和 1 在哪里呢?” Node.js Buffers 完整指南

那是因为Node.js使用十六进制系统显示字节。这样,每个字节都可以仅使用两位数表示(一对数字和字母是从0-9 和 “a” to “f”)

Node.js Buffers 完整指南

为什么需要 buffers?因为在 buffers 出现之前,在JavaScript中并没有简单的方式去处理二进制数据,你必须采用类似于字符串的primitives,这种方式是比较慢的,也没有专门的工具来处理二进制文件。所以Buffers 会被创建,且提供一些简单和高效的API可以操作位和字节。

buffers使用

让我们看看使用buffers可以做的一些事情。

你会注意到使用 buffer 有点类似于 JavaScript 中使用数组的方式。例如,你可以使用.slice(),concat().length 操作buffer。 缓冲区也是可迭代的,可以使用例如for-of之类的构造器迭代。

如果你是在计算机上操作示例,记住 Buffer 类是全局的。你不需要单独的引入。

译者注: 虽然 Buffer 类在全局作用域内可用,但仍然建议通过 import 或 require 语句显式地引用它

创建 buffers

有三种方法创建Buffers。

  • Buffer.from()
  • Buffer.alloc()
  • Buffer.allocUnsafe()

buffers在以前是使用 Buffer 类构造函数(例如,new Buffer() )创建的。此语法已被弃用。

Buffer.from()

使用buffer.from()是创建buffer 的最直接方法。它可接受字符串、数组、ArrayBuffer,或也可以是另一个 buffer 实例。根据传递的参数, Buffer.from() 将以不同的方式创建缓冲区。

传入字符串时,将创建一个包含该字符串的新缓冲区对象。默认情况下,它将使用 utf-8 作为编码解析你的输入(点击here查看支持的所有编码类型)

// 使用字符串"heya!"创建一个新缓冲区
// 如果第二个参数,没有传入编码类型,将使用默认 'utf-8' 编码类型
Buffer.from("heya!");

// 创建一个和上面相同的缓冲区,但是输入十六进制编码字符串
Buffer.from("6865796121", "hex");

你还可以将字节数组传给Buffer.from()。这里,我传入跟之前相同的字符串(“heya!”),但是使用十六进制的字符数组表示。

// Also writes 'heya!' to the buffer, but passes a array of bytes
Buffer.from([0x68, 0x65, 0x79, 0x61, 0x21]);

如果你不熟悉0xNN语法,则意味着 0x之后的字符应该解释为十六进制值。

将buffer实例传入 Buffer.from()时,Node.js 会复制该实例到当前的缓冲区中。由于新缓冲区会分配在不同的内存区域中,故你可以独立的修改它。

const buffer1 = Buffer.from('cars');
const buffer2 = Buffer.from(buffer1);

buffer2[0] = 0x6d;
console.log(buffer1.toString()); // --> "cars"
console.log(buffer2.toString()); // --> "mars"

这些应该覆盖了你使用 Buffer.from() 的大多数情况。详情可参考文档 docs

Buffer.alloc()

.alloc() 方法在您想要创建空缓冲区时很有用,不需要初始化数据填充。默认情况下,它接受一个数字并返回一个给定大小并且填充了 0 的缓冲区。

Buffer.alloc(6) // --> <Buffer 00 00 00 00 00 00>

你可以在之后填充你想要的任何数据。

const buff = Buffer.alloc(1);

buff[0] = 0x78;

console.log(buff.toString('utf-8')); // x

你还可以使用 0 以外的其他内容和给定的编码填充缓冲区。

Buffer.alloc(6, "X", "utf-8");

Buffer.allocUnsafe()

使用 .allocUnsafe(),可以跳过清理和用 0 填充 buffer 的过程。 buffer 将被分配在可能包含旧数据的内存区域中(这就是“Unsafe”的部分来源)。例如,以下代码很可能每次运行时都会打印一些随机数据

// 分配大小为 10000 的随机内存区域
// 不清理它(用 0 填充)所以它可能包含旧数据
const buff = Buffer.allocUnsafe(1000);

// 打印加载的随机数
console.log(buff.toString('utf-8'));

.allocUnsafe() 有一个好处的使用情况是当你复制一个被安全分配的缓冲区。由于你复制buffer 时是会完整的覆盖,所以所有旧字节数据都将被可预测的数据替换:

// Creates a buffer from a string
const buff = Buffer.from('hi, I am a safely allocated buffer');

// Creates a new empty buffer with `allocUnsafe` of the sameconst buffCopy = Buffer.allocUnsafe(buff2.length);
// length as the previous buffer. It will be initally filled with old data.
const buffCopy = Buffer.allocUnsafe(buff.length);

// Copies the original buffer into the new, unsafe buffer.
// Old data will be overwritten with the bytes
// from 'hi, I am a safely allocated buffer' string.
buff.copy(buffCopy);

console.log(buffCopy.toString());
// --> 'hi, I am a safely allocated buffer'

通常来说,.allocUnsafe()应当仅被使用在你有很好的理由使用的情况下(例如,性能优化)使用。每当使用它时,请确保永远不在没有使用新数据填充完整它的情况下返回 buffer 实例,否则你可能会泄漏敏感的信息。

写入buffers

Buffer.write()是将数据写入 buffers 的方法。 默认情况下,它将写入一个以 utf-8编码的、没有偏移(从 buffer 的第一个位置开始写入)的字符串。它会返回一个数字,是写入buffer中的字节数。

const buff = Buffer.alloc(9);

buff.write("hey there"); // 返回 9(写入的字节数)

// 如果写入的字节数超过缓冲区支持的字节数,
// 您的数据将被截断以适合缓冲区。
buff.write("hey christopher"); // retuns 9 (number of bytes written)

console.log(buff.toString());
// -> 'hey chris'

请记住,并非所有字符都会占用 buffer 中的单个字节 !

const buff = Buffer.alloc(2);

// 版权符号('©')占用两个字节,
//所以下面的操作将完全填满缓冲区
buff.write("©"); // returns 2

//如果缓冲区太小,无法存储字符,则不会写入。
const tinyBuff = Buffer.alloc(1);

tinyBuff.write("©"); // returns 0 (nothing was written)

console.log(tinyBuff);
// --> <Buffer 00> (empty buffer)

另外请注意到,2不是字符拥有最大的字节数。例如,utf-8编码类型支持最多4字节的字符。 由于无法修改缓冲区的大小,所以始终需要注意你正在编写的内容它将会占用多少空间(缓冲区的大小与内容的大小)。

另一个写入buffer的方法是通过类似于数组的语法 add方法,把字节添加到buffer的特殊位置。需要注意的是,任何超过 1 个字节的数据都需要分解并设置在buffer的每个位置

const buff = Buffer.alloc(5);

buff[0] = 0x68; // 0x68 is the letter "h"
buff[1] = 0x65; // 0x65 is the letter "e"
buff[2] = 0x6c; // 0x6c is the letter "l"
buff[3] = 0x6c; // 0x6c is the letter "l"
buff[4] = 0x6f; // 0x6f is the letter "o"

console.log(buff.toString());
// --> 'hello'

// ⚠️ 警告: 如果你尝试设置超过 2 个字节的字符到一个位置,它会失败
buff[0] = 0xc2a9; // 0xc2a9 is the symbol '©'

console.log(buff.toString());
// --> '�ello'

// 但是如果你分别写每个字节...
buff[0] = 0xc2;
buff[1] = 0xa9;

console.log(buff.toString());
// --> '©llo'

虽然你可以使用类似数组的语法写入buffers,但我建议你尽可能坚持使用 Buffer.from() 。管理输入的长度是一项艰巨的任务,并且会给你的代码带来复杂性。使用 .from(),您可以无担忧的在 buffer 中写入内容,并通过检查是否未写入任何内容(返回 0 时)来处理输入过大的情况。

迭代buffers

你可以使用类似于数组的现代JavaScript 结构去迭代 buffer。例如: 使用 for-of

const buff = Buffer.from('hello!');

for (const b of buff) {
   // `.toString(16)`返回十六进制格式内容
  console.log(b.toString(16));
}

其他的遍历方法 例如 .entries(), .values().keys()也同样也适用于 buffers,栗子:使用 .entries()

const buff = Buffer.from("hello!");
const copyBuff = Buffer.alloc(buff.length);

for (const [index, b] of buff.entries()) {
  copyBuff[index] = b;
}

console.log(copyBuff.toString());
// -> 'hello!'

走的更远: Buffers and TypeArrays

在 JavaScript(我的意思是一般的 JavaScript,不限于 Node.js)中,可以使用特殊的 ArrayBuffer 类分配内存。我们很少直接操作 ArrayBuffer 对象。相反,我们使用一组引用底层数组缓冲区的“视图”对象。这些视图对象是:

Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array等。

完整列表 请点击here

这些上面列出的视图对象统称为TypedArray。所有视图对象都通过原型从 TypedArray 继承方法。 TypedArray 构造函数不是全局公开的,必须始终使用其中一种视图方法。如果你看到一些使用 new TypedArray() 的教程或文档,这意味着它正在使用任何视图对象(Uint8Array、Float64Array 等)

在 Node.js 中,从 Buffer 类创建的对象也是 Uint8Array 的实例。它们之间有一些小的差异。你可以在此阅读 here

总结

作为初学者,buffers 是 Node.js 中的一个主题,让我感到非常困惑(另一个是流,但它值得拥有自己的帖子)。希望我能够揭开有关缓冲区的一些概念的神秘面纱,并概述 Buffer API。

我是废材壶,前端开发者,欢迎微信搜一搜「 CodeLife集」阅读不迷路

Node.js Buffers 完整指南

原文地址

上一篇:an upstream response is buffered to a temporary file


下一篇:Boost之vector<boost::asio::mutable_buffer>转char*