1. 程式人生 > 實用技巧 >Node.js Buffer使用詳解

Node.js Buffer使用詳解

一,開篇分析

NodeJS的開發語言是JavaScript,JavaScript語言自身只有字串資料型別,沒有二進位制資料型別。NodeJS有時會進行網路傳輸、檔案操作、圖片處理等操作,而這些操作都與二進位制資料緊密相關。因此,NodeJS提供了一個與String對等的全域性建構函式Buffer來提供對二進位制資料的操作。在 Node.js中Buffer 類用來建立一個專門存放二進位制資料的快取區。除了可以讀取檔案等操作得到Buffer的例項外,還能夠直接構造。

二、建立 Buffer 類

Buffer 提供了以下 API 來建立 Buffer 類:

  • Buffer.alloc(size[, fill[, encoding]]): 返回一個指定大小的 Buffer 例項,如果沒有設定 fill,則預設填滿 0
  • Buffer.allocUnsafe(size): 返回一個指定大小的 Buffer 例項,但是它不會被初始化,所以它可能包含敏感的資料
  • Buffer.allocUnsafeSlow(size)
  • Buffer.from(array): 返回一個被 array 的值初始化的新的 Buffer 例項(傳入的 array 的元素只能是數字,不然就會自動被 0 覆蓋)
  • Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一個新建的與給定的 ArrayBuffer 共享同一記憶體的 Buffer。
  • Buffer.from(buffer): 複製傳入的 Buffer 例項的資料,並返回一個新的 Buffer 例項
  • Buffer.from(string[, encoding]): 返回一個被 string 的值初始化的新的 Buffer 例項
// 建立一個長度為 10、且用 0 填充的 Buffer。
const buf1 = Buffer.alloc(10);

// 建立一個長度為 10、且用 0x1 填充的 Buffer。 
const buf2 = Buffer.alloc(10, 1);

// 建立一個長度為 10、且未初始化的 Buffer。
// 這個方法比呼叫 Buffer.alloc() 更快,
// 但返回的 Buffer 例項可能包含舊資料,
// 因此需要使用 fill() 或 write() 重寫。
const buf3 = Buffer.allocUnsafe(10);

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

// 建立一個包含 UTF-8 位元組 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
const buf5 = Buffer.from('tést');

// 建立一個包含 Latin-1 位元組 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
const buf6 = Buffer.from('tést', 'latin1');

三、寫入緩衝區

語法

寫入 Node 緩衝區的語法如下所示:

buf.write(string[, offset[, length]][, encoding])

引數

引數描述如下:

string - 寫入緩衝區的字串。

offset - 緩衝區開始寫入的索引值,預設為 0 。

length - 寫入的位元組數,預設為 buffer.length

encoding - 使用的編碼。預設為 'utf8' 。

根據 encoding 的字元編碼寫入 string 到 buf 中的 offset 位置。 length 引數是寫入的位元組數。 如果 buf 沒有足夠的空間儲存整個字串,則只會寫入 string 的一部分。 只部分解碼的字元不會被寫入。

返回值

返回實際寫入的大小。如果 buffer 空間不足, 則只會寫入部分字串。

例項

buf = Buffer.alloc(256);

len = buf.write("www.runoob.com");

console.log("寫入位元組數 : "+  len);

執行以上程式碼,輸出結果為:

$node main.js
寫入位元組數 : 14

四、從緩衝區讀取資料

語法

讀取 Node 緩衝區資料的語法如下所示:

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

引數

引數描述如下:

encoding - 使用的編碼。預設為 'utf8' 。

start - 指定開始讀取的索引位置,預設為 0。

end - 結束位置,預設為緩衝區的末尾。

返回值

解碼緩衝區資料並使用指定的編碼返回字串。

例項

buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
  buf[i] = i + 97;
}

console.log( buf.toString('ascii'));       // 輸出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5));   //使用 'ascii' 編碼, 並輸出: abcde
console.log( buf.toString('utf8',0,5));    // 使用 'utf8' 編碼, 並輸出: abcde
console.log( buf.toString(undefined,0,5)); // 使用預設的 'utf8' 編碼, 並輸出: abcde

執行以上程式碼,輸出結果為:

$ node main.js
abcdefghijklmnopqrstuvwxyz
abcde
abcde
abcde

五、將 Buffer 轉換為 JSON 物件

語法

將 Node Buffer 轉換為 JSON 物件的函式語法格式如下:

buf.toJSON()

當字串化一個 Buffer 例項時,JSON.stringify() 會隱式地呼叫該 toJSON()。

返回值

返回 JSON 物件。

例項

const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);

// 輸出: {"type":"Buffer","data":[1,2,3,4,5]}
console.log(json);

const copy = JSON.parse(json, (key, value) => {
 return value && value.type === 'Buffer' ?
  Buffer.from(value.data) :
  value;
});

// 輸出: <Buffer 01 02 03 04 05>
console.log(copy);

執行以上程式碼,輸出結果為:

{"type":"Buffer","data":[1,2,3,4,5]}
<Buffer 01 02 03 04 05>

六、聊聊Buffer

JavaScript對字串處理十分友好,無論是寬位元組還是單位元組字串,都被認為是一個字串。Node中需要處理網路協議、操作資料庫、處理圖片、檔案上傳等,還需要處理大量二進位制資料,自帶的字串遠不能滿足這些要求,因此Buffer應運而生。

Buffer結構

Buffer是一個典型的Javascript和C++結合的模組,效能相關部分用C++實現,非效能相關部分用javascript實現。

Node在程序啟動時Buffer就已經加裝進入記憶體,並將其放入全域性物件,因此無需require

Buffer物件:類似於陣列,其元素是16進位制的兩位數。

Buffer記憶體分配

Buffer物件的記憶體分配不是在V8的堆記憶體中,在Node的C++層面實現記憶體的申請。

為了高效的使用申請來得記憶體,Node中採用slab分配機制,slab是一種動態記憶體管理機制,應用各種*nix作業系統。slab有三種狀態:

(1) full:完全分配狀態

(2) partial:部分分配狀態

(3) empty:沒有被分配狀態

七、總結

(1)JavaScript適合處理Unicode編碼資料,但對二進位制資料的處理並不友好。

(2)所以處理TCP流或檔案系統時,對八位位元組流的處理很有必要。

(3)Node有幾個用於處理,建立和消耗八位位元組流的方法。

(4)原始資料存放在一個Buffer例項中,一個Buffer類似一個整數陣列,但是它的記憶體,分配在V8堆疊外。一個Buffer的大小是不能更改的。

(5)處理的編碼型別有:ascii,utf8,utf16le,ucs2(utf16le的別名),base64,binary,hex。

(6)Buffer為全域性元素,直接new Buffer()就得到一個Buffer例項。