[譯]一篇幫你徹底弄懂NodeJs中的Buffer

Vincent Ko發表於2018-10-09

前言:遇見前端,應該是今年最幸運的事情了。然而,幸運並未就此打住。

5月自己的第一份實習與唯品會邂逅

7月自己在掘金的兩篇文章點贊數過千

10月自己拿到了騰訊的offer

現在,我在準備自己的畢設,準備下一個階段的到來。掘金社群的確是一個讓人成長的地方,我也願意今後有時間繼續分享自己的成長經歷。這篇文章是昨天看到的,通俗易懂,實在佩服,忍不住翻譯過來和大家分享,繼續成長

更好的閱讀體驗,歡迎進入部落格檢視喲


[譯]一篇幫你徹底弄懂NodeJs中的Buffer
原文連結

你是不是和我一樣,對Node.js中的Buffer, Stream,二進位制資料一直都是很模糊的印象? 或者有的時候覺得,哎,我會用就行了,這些原理、底層的東西,應該交給Node.js的工程師們去理解。

的確,這些名詞可能會比較初學者感到恐懼和陌生,特別是那些剛從前端轉全棧,做Node.js,卻沒有計算機基礎的同學來說。

但是很遺憾,很多教程或者書籍都會直接跳過這些原理和解釋的部分,直接教你怎麼使用Node.js的一些庫、工具或者API,但是對於核心的部分、為什麼這樣處理和使用,卻隻字未提。甚至有些直接告訴你:“你根本不需要理解這些,因為你在工作中可能永遠不會直接使用它”

是的,如果你想一輩子做一個平庸的程式設計師,的確可以在工作中不直接使用。

然而,如果那些迷惑和模糊的概念,能引起你的好奇,並不斷保持這種好奇心去學習和探索,那麼你對Node.js的理解就會更上一層樓,然後你就會更願意去學習和了解Node.js一些核心的、原理性的東西,比如Buffer, Stream。 這也就是我寫這篇文章的原因--去幫助你更好的、更深入的去理解Node.js。

當說到Buffer,官方是這麼說的:

...JavaScript 語言沒有讀取或操作二進位制資料流的機制。 Buffer 類被引入作為 Node.js API 的一部分,使其可以在 TCP 流或檔案系統操作等場景中處理二進位制資料流。

嗯..尷尬,除非你已經有一些計算機基礎,否則上面這句話說了只能讓你腦袋更大。我們嘗試簡化一下,把主要含義提煉一下,可以這麼說:

Buffer類被引入到Node.js的API中,讓其與二進位制資料流的操作和互動成為可能

這樣是不是簡單的多了? 但是...Buffer,streams和二進位制資料又是什麼東西呢?我們從後向前,一個一個解釋下。

二進位制資料是什麼鬼?

你應該已經知道,計算機儲存和表示資料使用二進位制的。比如,下面這些是5個二進位制數,5個不同的1和0序列:

10, 01, 001, 1110, 00101011

二進位制中的每個數字,0或1叫做位(bit),也就是Binary digIT的縮寫。

為了能夠儲存和表示這些資料,計算機需要將資料轉換為二進位制形式。比如,要儲存數字12,計算機需要將12轉化為二進位制1100

計算機怎麼知道要如何去轉換?這就完全是一個數學問題了。計算機是知道怎麼去處理的,有興趣的可以自己查閱。

但是,我們日常工作的資料型別不僅僅是數字。我們還有字元、圖片甚至視訊。計算機是知道如何將這些表示為二進位制的。就拿字元來說,比如計算機如何用二進位制來表示”L“這個字母。為了將資料儲存為二進位制形式,無論任何型別的資料都會先被轉換為數字,然後將數字轉為二進位制形式。所以為了表示”L“,計算機首先將L轉換為數字表示,我們看下怎麼做到這一點。

開啟你的瀏覽器控制檯,然後貼上下面的程式碼:"L".charCodeAt(0)。你看到了什麼?數字76?這就是字母L的數字編碼。但是計算機怎麼知道具體哪個數字代表那個字母呢?

字符集

字符集就是定義數字所代表的字元的一個規則表,同樣定義了怎樣用二進位制儲存和表示。那麼,用多少位來表示一個數字,這個就叫字元編碼(Character Encoding)

有一種字元編碼叫做UTF-8。它規定了,字元應該以位元組為單位來表示。一個位元組是8位(bit)。所以8個1和0組成的序列,應該用二進位制來儲存和表示任意一個字元。

為了更好的理解,舉個例子: 比如之前提到的12的二進位制表示是1100。 所以,使用UTF-8的格式來表示,應該使用一個位元組,也就是8位來完整表示,也即00001100, 沒有錯吧?

因此,76在計算機中的儲存形式應該是01001100

這就是計算機將字元儲存成二進位制的方式。當然,計算機也有一些特殊規則,將圖片、視訊等儲存為二進位制的,總之,計算機會將無論圖片、視訊或其他資料都轉換為二進位制並儲存,這就是我們說的二進位制資料

如果你對字元編碼非常感興趣,那你可以參考一下這篇文章

現在我們瞭解了什麼是二進位制資料,但是我們介紹buffer的時候,說的**二進位制資料流(streams of binary data)**又是什麼呢?

Stream

在Node.js中,流(stream)就是一系列從A點到B點移動的資料。完整點的說,就是當你有一個很大的資料需要傳輸、搬運時,你不需要等待所有資料都傳輸完成才開始下一步工作。

實際上,巨型資料會被分割成小塊(chunks)進行傳輸。所以,buffer的原始定義中所說的(“streams of binary data… in the context of… file system”)意思就是說二進位制資料在檔案系統中的傳輸。比如,將file1.txt的文字儲存到file2.txt中。

但是,buffer到底在流(stream)中,是如何操作二進位制資料的?buffer到底是個什麼呢?

Buffer

我們已經知道資料流(stream of data)是從一個地方向另一個地方傳輸資料的過程,但是這個具體是怎麼樣的一個過程?

通常情況下,我們傳輸資料往往是為了處理它,或者讀它,或者基於這些資料做處理等。但是,在每次傳輸過程中,有一個資料量的問題。因此當資料到達的時間比資料理出的時間快的時候,這個時候我們處理資料就需要等待了。

領域覅那個面,如果處理資料的時間比到達的時間快,這一時刻僅僅到達了一小部分資料,那這小部分資料需要等待剩下的資料填滿,然後再送過去統一處理。

這個"等待區域"就是buffer! 它是你電腦上的一個很小的實體地址,一般在RAM中,在這裡資料暫時的儲存、等待,最後在流(stream)中,傳送過去並處理。

我們可以把整個流(stream)和buffer的配合過程看作公交站。在一些公交站,公車在沒有裝滿乘客前是不會發車的,或者在特定的時刻才會發車。當然,乘客也可能在不同的時間,人流量大小也會有所不同,有人多的時候,有人少的時候,乘客或公交站都無法控制人流量。

不論何時,早到的乘客都必須等待,直到公車接到指令可以發車。當乘客到站,發現公車已經裝滿,或者已經開走,他就必須等待下一班車次。

總之,這裡總會有一個等待的地方,這個等待的區域就是Node.js中的Buffer Node.js不能控制資料什麼時候傳輸過來,傳輸速度,就好像公交車站無法控制人流量一樣。他只能決定什麼時候傳送資料。如果時間還不到,那麼Node.js就會把資料放入buffer--"等待區域"中,一個在RAM中的地址,直到把他們傳送出去進行處理。

一個關於buffer很典型的例子,就是你線上看視訊的時候。如果你的網路足夠快,資料流(stream)就可以足夠快,可以讓buffer迅速填滿然後傳送和處理,然後處理另一個,再傳送,再另一個,再傳送,然後整個stream完成。

但是當你網路連線很慢,當處理完當前的資料後,你的播放器就會暫停,或出現"緩衝"(buffer)字樣,意思是正在收集更多的資料,或者等待更多的資料到來,才能下一步處理。當buffer裝滿並處理好,播放器就會顯示資料,也就是播放視訊了。在播放當前內容的時候,更多的資料也會源源不斷的傳輸、到達和在buffer等待。

如果播放器已經處理完或播放完前一個資料,buffer仍然沒有填滿,"buffering"(緩衝)字元就會再次出現,等待和收集更多的資料。

這就是Buffer!

從原始的定義,我們知道,buffer可以在stream中與二進位制資料進行互動和操作。那麼到底可以進行什麼樣的操作呢?在Node.js中又應該如何進行剛才所描述的一些東西呢?我們來瞧一瞧。

與Buffer共舞

你甚至可以做你自己的buffer! 在stream中,Node.js會自動幫你建立buffer之外,你可以建立自己的buffer並操作它,是不是很有趣? 我們來搞一個!

根據你的需求,這裡有不同的建立方式,我們一起看一下吧:

// 建立一個大小為10的空buffer
// 這個buffer只能承載10個位元組的內容

const buf1 = Buffer.alloc(10);

// 根據內容直接建立buffer

const buf2 = Buffer.from("hello buffer");
複製程式碼

建立了之後,你就可以操作buffer了

// 檢查下buffer的結構

buf1.toJSON()
// { type: 'Buffer', data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }
// 一個空的buffer

buf2.toJSON()
// { type: 'Buffer',data: [ 104, 101, 108, 108, 111, 32, 98, 117, 102, 102, 101, 114 ] }
// the toJSON() 方法可以將資料進行Unicode編碼並展示
   
// 檢查buffer的大小

buf1.length // 10

buf2.length //12 根據資料自動盛滿並建立

//寫入資料到buffer
buf1.write("Buffer really rocks!")

//解碼buffer

buf1.toString() // 'Buffer rea'

//哦豁,因為buf1只能承載10個位元組的內容,所有多處的東西會被截斷

//比較兩個buffers
複製程式碼

當然,在Node.js中,還有更多更豐富的方法來操作buffer,你可以參考這裡,然後去嘗試更多的方法。

最後,我想給你一個小小的挑戰:去閱讀zlib.js的原始碼,一個Node.js的核心庫,去看一下它是如何利用buffer這個神器去操作二進位制資料流的。處理後,最後變成gziped檔案。 當你在閱讀的時候,記錄下你的學習經歷並在評論中分享下來吧。

我希望這個介紹可以幫你更好的理解Node.js中的Buffer。

如果你覺得我這篇文章還不錯,為了能讓更多人看到,請點個贊吧,可以讓這篇文章更好的傳播,讓更多人看到。

如果你有任何問題,或者有不同的理解,請盡情的評論提出或者通過twitter找我哦。

相關文章