写点什么

【译】Node.js Buffers 完整指南

用户头像
废材壶
关注
发布于: 5 小时前
【译】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 使用十六进制系统显示字节。这样,每个字节都可以仅使用两位数表示(一对数字和字母是从 0-9 和 "a" to "f")



为什么需要 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 bytesBuffer.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 stringconst 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 集」阅读不迷路


原文地址

用户头像

废材壶

关注

还未添加个人签名 2018.06.09 加入

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

评论

发布
暂无评论
【译】Node.js Buffers 完整指南