NodeJS文檔之Buffer(1)-Buffer的簡介

Buffer

在ECMAScript 2015(ES6)中引入TypedArray以前,JavaScript中沒有讀取或操做二進制數據流的機制。 Buffer類做爲Node.js標準API的一部分被引入,使得能夠在諸如TCP流和文件系統操做的上下文中經過8位的字節流進行交互。javascript

如今TypedArray已經添加到ES6中,Buffer類以一種更優化和適用於Node.js的方式實現了Uint8Array的API。java

Buffer類的實例似於整數數組,不過是對應V8堆外的固定大小的原始內存進行分配(堆外內存,不受V8引擎控制)。 緩衝區的大小在建立時已經肯定,不能調整大小。node

Buffer類是Node.js中的一個全局變量,所以不須要使用require('buffer').Buffer數組

// 建立一個長度爲10的『0填充』Buffer實例
const buf1 = Buffer.alloc(10);

// 建立一個長度爲10的Buffer,填充0x1
const buf2 = Buffer.alloc(10, 0x1);

// 建立一個長度爲10的未初始化緩衝區
// 這個方法比使用Buffer.alloc()快,可是返回的Buffer實例可能包含以前的老數據。
// 須要使用fill()或write()重寫。
const buf3 = Buffer.allocUnsafe(10);

// 建立一個包含[0x1, 0x2, 0x3]的緩衝區
const buf4 = Buffer.from([1, 2, 3]);

// 建立一個包含UTF-8字節[0x74, 0xc3, 0xa9, 0x73, 0x74]的緩衝區
const buf5 = Buffer.from('tést');

// 建立一個包含Latin-1字節[0x74, 0xe9, 0x73, 0x74]的緩衝區
const buf6 = Buffer.from('tést', 'latin-1');

Buffer.from(),Buffer.alloc(),Buffer.allocUnsafe()

在v6以前的Node.js版本中,使用Buffer構造函數建立了Buffer實例,該函數根據提供的參數以不一樣的方式分配返回的Buffer:瀏覽器

  • 將一個數字做爲第一個參數傳遞給Buffer() (例如new Buffer(10)),分配一個指定大小的新的Buffer對象。 爲這些緩衝區實例分配的內存不會被初始化,而且可能包含敏感數據。 這種緩衝區實例必須經過使用buf.fill(0)或徹底寫入緩衝區來手動初始化。 雖然這種行爲是爲了提升性能,可是開發經驗代表,建立一個快速但未初始化的緩衝區和建立一個更慢但更安全的緩衝區之間須要更明確的區分。安全

  • 傳遞字符串,數組或Buffer做爲第一個參數將傳遞的對象的數據複製到緩衝區中。bash

  • 傳遞ArrayBuffer返回一個與給定的ArrayBuffer共享已分配內存的Buffer。服務器

由於new Buffer()的行爲是基於第一個參數傳遞的值的類型來選擇具體操做,若是沒有傳遞正確的參數給給new Buffer()或未能適當地初始化新分配的Buffer中緩衝區內容。那麼此類應用將在無心中將安全性和可靠性的相關問題引入到他們的代碼中。函數

爲了使緩衝區實例的建立更可靠,出現更少的錯誤,new Buffer()造函數的這種形式已被棄用,使用Buffer.from()Buffer.alloc()Buffer.allocUnsafe()方法來代替。性能

開發人員應該將如今使用的new Buffer()函數遷移到如下這些新的API之一。

  • Buffer.from(array)返回一個包含所提供的八位字節的副本的新緩衝區。

  • Buffer.from(arrayBuffer [,byteOffset [,length]])返回一個新的緩衝區,它與給定的ArrayBuffer共享相同的分配內存。

  • Buffer.from(buffer)返回一個包含給定Buffer的內容的副本的新緩衝區。

  • Buffer.from(string [,encoding])返回一個包含所提供字符串副本的新緩衝區。

  • Buffer.alloc(size [,fill [,encoding]])返回指定大小的「已填充」Buffer實例。 此方法雖然明顯慢於Buffer.allocUnsafe(size)方法,但它能夠確保新建立的Buffer實例不會包含舊的和潛在的敏感數據。

  • Buffer.allocUnsafe(size)Buffer.allocUnsafeSlow(size)都返回一個指定大小的新的緩衝區,其內容必須使用buf.fill(0)初始化或徹底寫入新的內容以覆蓋舊的敏感數據。

Buffer.allocUnsafe()返回的緩衝區實例的大小小於或等於Buffer.poolSize的一半那麼能夠在內部共享內存池中進行分配。 而Buffer.allocUnsafeSlow()返回的實例不會使用內部共享內存池。


--zero-fill-buffers命令行參數

Node.js可使用--zero-fill-buffers命令行參數啓動,強制全部新分配的Buffer實例在使用new Buffer(size)Buffer.allocUnsafe()Buffer.allocUnsafeSlow()或者new SlowBuffer()在建立時自動0。 使用此參數會更改這些方法的默認行爲,而且會對性能產生重大影響。 建議僅在必要時強制使用--zero-fill-buffers選項,以確保新分配的緩衝區實例不會包含潛在敏感數據。

$ node --zero-fill-buffers
> Buffer.allocUnsafe(5);
<Buffer 00 00 00 00 00>

爲何Buffer.allocUnsafe()Buffer.allocUnsafeSlow()不安全?

當調用Buffer.allocUnsafe()Buffer.allocUnsafeSlow()時,分配的內存段未初始化(不歸0)。 雖然該設計使得存儲器的分配至關快,可是所分配的存儲器段可能包含潛在敏感的舊數據。 使用由Buffer.allocUnsafe()建立的緩衝區而不徹底從新覆蓋的內存可能在讀取緩衝區內存時泄漏這些舊數據。

雖然使用Buffer.allocUnsafe()有明顯的性能優點,但必須格外當心,以免將安全漏洞引入應用程序。


Buffers和字符編碼

Buffer實例一般用於表示編碼字符的序列,例如UTF-8,UCS2,Base64甚至Hex編碼數據。 能夠經過使用字符編碼聲明字符串在緩衝區實例和普通JavaScript字符串之間來回轉換。

const buf = Buffer.from('hello world', 'ascii');

console.log(buf.toString('hex')); // => 68656c6c6f20776f726c64

console.log(buf.toString('base64')); // => aGVsbG8gd29ybGQ=

Node.js當前支持的字符編碼包括:

  • ascii

  • utf8

  • utf16le

  • ucs2

  • base64

  • latin1

  • binary

  • hex

注意:當今的瀏覽器遵循WHATWG規範,將「latin1」和ISO-8859-1都合併到win-1252。 這意味着,當作相似於http.get()的操做時,若是返回的字符集是在WHATWG規範列出的範圍內,服務器可能實際返回的是win-1252的編碼數據,此時使用「latin1」編碼可能會錯誤地解碼字符,從而出現亂碼。


Buffers和TypeArray

Buffer實例也是Uint8Array實例。 可是,與ECMAScript 2015中的TypedArray規範有一點點的不兼容。例如,當ArrayBuffer#slice()建了一個slice的副本時,Buffer#slice()實現建立了一個沒有複製的現有Buffer的副本,使得Buffer#slice()更有效率。

Buffer中建立新的TypedArray實例時須要注意如下事項:

  1. Buffer對象的內存是從TypedArray複製過來的,不是共享。

  2. Buffer對象的內存能夠解釋爲不一樣元素的數組,而不是目標類型的字節數組。 也就是說,new Uint32Array(Buffer.from([1,2,3,4]))是經過數組[1,2,3,4]建立一個長度爲4的Uint32Array,而不是經過[0x1020304][0x4030201]建立一個長度爲1的Uint32Array

能夠經過使用TypeArray對象的.buffer屬性建立一個新的Buffer,它與TypedArray實例共享相同的分配內存。

const arr = new Uint16Array(2);

arr[0] = 5000;
arr[1] = 4000;

const buf1 = Buffer.from(arr);

const buf2 = Buffer.from(arr.buffer);

console.log(buf1); // => <Buffer 88 a0>

console.log(buf2); // => <Buffer 88 13 a0 0f>

arr[1] = 6000;

console.log(buf1); // => <Buffer 88 a0>

console.log(buf2); // => <Buffer 88 13 70 17>

注意,當使用TypedArray的.buffer建立一個Buffer時,能夠經過傳遞byteOffsetlength參數來使用ArrayBuffer的一部份內容。

const arr = new Uint16Array(20);
const buf = new Buffer.from(arr.buffer, 0, 16);

console.log(buf.length); // => 16

Buffer.from()TypedArray.from()有不一樣的函數簽名和實現。 具體來講,TypedArray變量接受第二個參數,它是在類型數組的每一個元素上調用的映射函數:

  • TypedArray.from(source[, mapFn[, thisArg]])

Float32Array.from([1, 2, 3], x => x + x);      
// Float32Array [ 2, 4, 6 ]

然而,Buffer.from()方法不支持使用映射函數:

  • Buffer.from(array)

  • Buffer.from(buffer)

  • Buffer.from(arrayBuffer[, byteOffset [, length]])

  • Buffer.from(string[, encoding])


Buffers和ES6的迭代器

可使用ECMAScript 2015(ES6)for..of語法對緩衝區實例進行迭代。

const buf = Buffer.from([1, 2, 3]);

for (const b of buf) {
    console.log(b)); // => 1 2 3
}

此外,buf.values()buf.keys()buf.entries()方法均可用於建立迭代器。

相關文章
相關標籤/搜索