這篇文章主要介紹了Node.js中Stream是什么,具有一定借鑒價值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
成都創(chuàng)新互聯(lián)云計算的互聯(lián)網(wǎng)服務(wù)提供商,擁有超過13年的服務(wù)器租用、托管服務(wù)器、云服務(wù)器、網(wǎng)絡(luò)空間、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗,已先后獲得國家工業(yè)和信息化部頒發(fā)的互聯(lián)網(wǎng)數(shù)據(jù)中心業(yè)務(wù)許可證。專業(yè)提供云主機、網(wǎng)絡(luò)空間、域名與空間、VPS主機、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。對于大部分有后端經(jīng)驗的的同學(xué)來說 Stream 對象是個再合理而常見的對象,但對于前端同學(xué) Stream 并不是那么理所當然,github 上甚至有一篇 9000 多 Star 的文章介紹到底什么是 Stream —— stream-handbook(/tupian/20230522/stream-handbook)。為了更好的理解 Stream,在這篇文章的基礎(chǔ)上簡單總結(jié)概括一下。
在 Unix 系統(tǒng)中流就是一個很常見也很重要的概念,從術(shù)語上講流是對輸入輸出設(shè)備的抽象。
ls | grep *.js
類似這樣的代碼我們在寫腳本的時候經(jīng)??梢杂龅?,使用|
連接兩條命令,把前一個命令的結(jié)果作為后一個命令的參數(shù)傳入,這樣數(shù)據(jù)像是水流在管道中傳遞,每個命令類似一個處理器,對數(shù)據(jù)做一些加工,因此| 被稱為 “管道符號”。
從程序角度而言流是有方向的數(shù)據(jù),按照流動方向可以分為三種流
設(shè)備流向程序:readable
程序流向設(shè)備:writable
雙向:duplex、transform
NodeJS 關(guān)于流的操作被封裝到了 Stream 模塊,這個模塊也被多個核心模塊所引用。按照 Unix 的哲學(xué):一切皆文件,在 NodeJS 中對文件的處理多數(shù)使用流來完成
普通文件
設(shè)備文件(stdin、stdout)
網(wǎng)絡(luò)文件(http、net)
有一個很容易忽略的知識點:在 NodeJS 中所有的 Stream 都是 EventEmitter 的實例。
我們寫程序忽然需要讀取某個配置文件config.json,這時候簡單分析一下
我們應(yīng)該使用 readable 流來做此事
const fs = require('fs'); const FILEPATH = '...'; const rs = fs.createReadStream(FILEPATH);
通過 fs 模塊提供的createReadStream()
方法我們輕松的創(chuàng)建了一個可讀的流,這時候 config.json 的內(nèi)容從設(shè)備流向程序。我們并沒有直接使用 Stream 模塊,因為 fs 內(nèi)部已經(jīng)引用了 Stream 模塊,并做了封裝。
有了數(shù)據(jù)后我們需要處理,比如需要寫到某個路徑 DEST ,這時候我們遍需要一個 writable 的流,讓數(shù)據(jù)從程序流向設(shè)備。
const ws = fs.createWriteStream(DEST);
兩種流都有了,也就是兩個數(shù)據(jù)加工器,那么我們?nèi)绾瓮ㄟ^類似 Unix 的管道符號|
來鏈接流呢?在 NodeJS 中管道符號就是pipe()
方法。
const fs = require('fs'); const FILEPATH = '...'; const rs = fs.createReadStream(FILEPATH); const ws = fs.createWriteStream(DEST); rs.pipe(ws);
這樣我們利用流實現(xiàn)了簡單的文件復(fù)制功能,關(guān)于 pipe() 方法的實現(xiàn)原理后面會提到,但有個值得注意地方:數(shù)據(jù)必須是從上游 pipe 到下游,也就是從一個 readable 流 pipe 到 writable 流。
上面提到了 readable 和 writable 的流,我們稱之為加工器,其實并不太恰當,因為我們并沒有加工什么,只是讀取數(shù)據(jù),然后存儲數(shù)據(jù)。
如果有個需求,把本地一個package.json 文件中的所有字母都改為小寫,并保存到同目錄下的 package-lower.json 文件下。
這時候我們就需要用到雙向的流了,假定我們有一個專門處理字符轉(zhuǎn)小寫的流 lower,那么代碼寫出來大概是這樣的
const fs = require('fs'); const rs = fs.createReadStream('./package.json'); const ws = fs.createWriteStream('./package-lower.json'); rs.pipe(lower).pipe(ws);
這時候我們可以看出為什么稱 pipe() 連接的流為加工器了,根據(jù)上面說的,必須從一個 readable 流 pipe 到 writable 流:
有點推理的趕腳呢,能夠滿足我們需求的 lower 必須是雙向的流,具體使用 duplex 還是 transform 后面我們會提到。
當然如果我們還有額外一些處理動作,比如字母還需要轉(zhuǎn)成 ASCII 碼,假定有一個流 ascii 那么我們代碼可能是
rs.pipe(lower).pipe(acsii).pipe(ws);
同樣 ascii 也必須是雙向的流。這樣處理的邏輯是非常清晰的,那么除了代碼清晰,使用流還有什么好處呢?
有個用戶需要在線看視頻的場景,假定我們通過 HTTP 請求返回給用戶電影內(nèi)容,那么代碼可能寫成這樣
const http = require('http'); const fs = require('fs'); http.createServer((req, res) => { fs.readFile(moviePath, (err, data) => { res.end(data); }); }).listen(8080);
這樣的代碼又兩個明顯的問題
電影文件需要讀完之后才能返回給客戶,等待時間超長
電影文件需要一次放入內(nèi)存中,相似動作多了,內(nèi)存吃不消
用流可以講電影文件一點點的放入內(nèi)存中,然后一點點的返回給客戶(利用了 HTTP 協(xié)議的 Transfer-Encoding: chunked 分段傳輸特性),用戶體驗得到優(yōu)化,同時對內(nèi)存的開銷明顯下降
const http = require('http'); const fs = require('fs'); http.createServer((req, res) => { fs.createReadStream(moviePath).pipe(res); }).listen(8080);
除了上述好處,代碼優(yōu)雅了很多,拓展也比較簡單。比如需要對視頻內(nèi)容壓縮,我們可以引入一個專門做此事的流,這個流不用關(guān)心其它部分做了什么,只要是接入管道中就可以了
const http = require('http'); const fs = require('fs'); const oppressor = require(oppressor); http.createServer((req, res) => { fs.createReadStream(moviePath) .pipe(oppressor) .pipe(res); }).listen(8080);
可以看出來,使用流后,我們的代碼邏輯變得相對獨立,可維護性也會有一定的改善,關(guān)于幾種流的具體使用方式且聽下回分解。
感謝你能夠認真閱讀完這篇文章,希望小編分享Node.js中Stream是什么內(nèi)容對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問題就找創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,詳細的解決方法等著你來學(xué)習(xí)!