理解node中的Buffer

先上文檔連接buffer緩衝器html

背景

在node應用程序中,咱們須要處理網絡協議,文件流數據等,在網絡流與文件的操做中,還有二進制數據。js中的string明顯不能知足這些需求。而buffer對象爲咱們提供了操做二進制流數據的能力。node

在引入 TypedArray 以前,JavaScript 語言沒有用於讀取或操做二進制數據流的機制。 Buffer 類是做爲 Node.js API 的一部分引入的,用於在 TCP 流、文件系統操做、以及其餘上下文中與八位字節流進行交互。
Buffer 類的實例相似於從 0 到 255 之間的整數數組(其餘整數會經過 & 255 操做強制轉換到此範圍),但對應於 V8 堆外部的固定大小的原始內存分配。 Buffer 的大小在建立時肯定,且沒法更改。
Buffer 類在全局做用域中,所以無需使用 require('buffer').Buffer。 --- 摘自文檔git

Buffer簡介

簡單的介紹一下Buffer,它的元素爲16進制的兩位數(即10進制的0-255之間),以下圖 github

危險的Buffer構造函數

值得注意的是,在node中,new Buffer('xxx')Buffer('xxx')由於存在着安全性問題,在v6的時候就被廢棄了。因此咱們須要使用Buffer.from或者Buffer.alloc 來替代。這裏建議啓用ESLint規則no-buffer-constructornode/no-deprecated-api以免意外的不安全Buffer API使用。
那麼,Buffer構造函數有什麼問題嗎?
原來,在node V8以前,new Buffer(50)將佔有50個字節,假如這個做爲CGI接受用戶的輸入,輸入500000000,會佔用500MB的內存,這麼大的內存佔用,很快就會內存溢出,致使服務崩潰。而現有的Buffer.from(5000000000)將會拋出異常。api

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type number
複製代碼

內存的分配

Buffer對象的內存分配不是在V8的堆內存中,而是在Node的C++層面實現的內存的申請。當進行小而頻繁的Buffer操做時,採用slab(一種動態內存管理的機制)機制進行預先申請和過後分配,使得js到操做系統直接沒有過多的內存申請的系統調用。對於大Buffer而言,則是直接使用C++層面提供的內存,無需細膩的分配操做。(區別大小Buffer的界限爲8Kb)數組

Buffer轉換

可轉換類型

Buffer對象可與字符串之間相互轉換。支持的類型以下:瀏覽器

  • 'ascii': 僅適用於 7 位 ASCII 數據。此編碼速度很快,若是設置則會剝離高位。安全

  • 'utf8': 多字節編碼的 Unicode 字符。許多網頁和其餘文檔格式都使用 UTF-8。bash

  • 'utf16le': 2 或 4 個字節,小端序編碼的 Unicode 字符。支持代理對(U+10000 至 U+10FFFF)。網絡

  • 'ucs2': 'utf16le' 的別名。

  • 'base64': Base64 編碼。當從字符串建立 Buffer 時,此編碼也會正確地接受 RFC 4648 第 5 節中指定的 「URL 和文件名安全字母」。

  • 'latin1': 一種將 Buffer 編碼成單字節編碼字符串的方法(由 RFC 1345 中的 IANA 定義,第 63 頁,做爲 Latin-1 的補充塊和 C0/C1 控制碼)。

  • 'binary': 'latin1' 的別名。

  • 'hex': 將每一個字節編碼成兩個十六進制的字符。

如何轉換

如下是一個Buffer對象與字符串相互轉換的例子

const buf = Buffer.from('hello world', 'ascii');
console.log(buf.toString('hex'));
// 打印: 68656c6c6f20776f726c64
console.log(buf.toString('base64'));
// 打印: aGVsbG8gd29ybGQ=
複製代碼

不支持的類型及處理方式

現有的編碼那麼多,Buffer只提供了幾種,而Buffer.isEncoding則爲咱們提供了編碼是否支持轉換的能力。對於不支持的類型,咱們只能藉助額外的模塊來解決了。例如iconv和iconv-lite

應用場景

I/O操做

const fs = require('fs');
const inputStream = fs.createReadStream('input.txt'); // 建立可讀流
const outputStream = fs.createWriteStream('output.txt'); // 建立可寫流
inputStream.pipe(outputStream); // 管道讀寫
複製代碼

zlib.js

zlib.js 爲 Node.js 的核心庫之一,其利用了緩衝區(Buffer)的功能來操做二進制數據流,提供了壓縮或解壓功能。參考源代碼 zlib.js 源碼

var http = require('http');
var zlib = require('zlib');
var fs = require('fs');
var filepath = './extra/fileForGzip.html';

var server = http.createServer(function(req, res){
    var acceptEncoding = req.headers['accept-encoding'];
    var gzip;

    if(acceptEncoding.indexOf('gzip')!=-1){    // 判斷是否須要gzip壓縮

        gzip = zlib.createGzip();

        // 記得響應 Content-Encoding,告訴瀏覽器:文件被 gzip 壓縮過
        res.writeHead(200, {
            'Content-Encoding': 'gzip'
        });
        fs.createReadStream(filepath).pipe(gzip).pipe(res);

    }else{

        fs.createReadStream(filepath).pipe(res);
    }

});

server.listen('3000');
複製代碼

總結

Buffer在node中有着極其重要的地位,流數據的處理都離不開它。在使用時咱們用Buffer.from取代了new Buffer。Buffer的內存分配用到了slab機制進行預先申請和過後分配。在性能上,buffer比string有着更加優異的表現,緣由是流數據自己就是二進制數據,不須要作額外的轉換就能夠傳輸。

Reference

相關文章
相關標籤/搜索