淺析Node.js 中 Stream API 的使用
本文由淺入深給大家介紹node.js stream api,具體詳情請(qǐng)看下文吧。
基本介紹
在 Node.js 中,讀取文件的方式有兩種,一種是用 fs.readFile ,另外一種是利用 fs.createReadStream 來(lái)讀取。
fs.readFile 對(duì)于每個(gè) Node.js 使用者來(lái)說(shuō)最熟悉不過(guò)了,簡(jiǎn)單易懂,很好上手。但它的缺點(diǎn)是會(huì)先將數(shù)據(jù)全部讀入內(nèi)存,一旦遇到大文件的時(shí)候,這種方式讀取的效率就非常低下了。
而 fs.createReadStream 則是通過(guò) Stream 來(lái)讀取數(shù)據(jù),它會(huì)把文件(數(shù)據(jù))分割成小塊,然后觸發(fā)一些特定的事件,我們可以監(jiān)聽(tīng)這些事件,編寫特定的處理函數(shù)。這種方式相對(duì)上面來(lái)說(shuō),并不好上手,但它效率非常高。
事實(shí)上, Stream 在 Node.js 中并非僅僅用在文件處理上,其他地方也可以看到它的身影,如 process.stdin/stdout , http , tcp sockets , zlib , crypto 等都有用到。
本文是我學(xué)習(xí) Node.js 中的 Stream API 中的一點(diǎn)總結(jié),希望對(duì)大家有用。
特點(diǎn)
基于事件通訊
可以通過(guò) pipe 來(lái)連接流
種類
Readable Stream 可讀數(shù)據(jù)流
Writeable Stream 可寫數(shù)據(jù)流
Duplex Stream 雙向數(shù)據(jù)流,可以同時(shí)讀和寫
Transform Stream 轉(zhuǎn)換數(shù)據(jù)流,可讀可寫,同時(shí)可以轉(zhuǎn)換(處理)數(shù)據(jù)
事件
可讀數(shù)據(jù)流的事件
readable 數(shù)據(jù)向外流時(shí)觸發(fā)
data 對(duì)于那些沒(méi)有顯式暫停的數(shù)據(jù)流,添加data事件監(jiān)聽(tīng)函數(shù),會(huì)將數(shù)據(jù)流切換到流動(dòng)態(tài),盡快向外提供數(shù)據(jù)
end 讀取完數(shù)據(jù)時(shí)觸發(fā)。注意不能和 writeableStream.end() 混淆,writeableStream 并沒(méi)有 end 事件,只有 .end() 方法
close 數(shù)據(jù)源關(guān)閉時(shí)觸發(fā)
error 讀取數(shù)據(jù)發(fā)生錯(cuò)誤時(shí)觸發(fā)
可寫數(shù)據(jù)流的事件
drain writable.write(chunk) 返回 false 之后,緩存全部寫入完成,可以重新寫入時(shí)就會(huì)觸發(fā)
finish 調(diào)用 .end 方法時(shí),所有緩存的數(shù)據(jù)釋放后觸發(fā),類似于可讀數(shù)據(jù)流中的 end 事件,表示寫入過(guò)程結(jié)束
pipe 作為 pipe 目標(biāo)時(shí)觸發(fā)
unpipe 作為 unpipe 目標(biāo)時(shí)觸發(fā)
error 寫入數(shù)據(jù)發(fā)生錯(cuò)誤時(shí)觸發(fā)
狀態(tài)
可讀數(shù)據(jù)流有兩種狀態(tài): 流動(dòng)態(tài) 和 暫停態(tài) ,改變數(shù)據(jù)流狀態(tài)的方法如下:
暫停態(tài) -> 流動(dòng)態(tài)
添加 data 事件的監(jiān)聽(tīng)函數(shù)
調(diào)用 resume 方法
調(diào)用 pipe 方法
注意:如果轉(zhuǎn)為流動(dòng)態(tài)時(shí),沒(méi)有 data 事件的監(jiān)聽(tīng)函數(shù),也沒(méi)有 pipe 方法的目的地,那么數(shù)據(jù)將遺失。
流動(dòng)態(tài) -> 暫停態(tài)
不存在 pipe 方法的目的地時(shí),調(diào)用 pause 方法
存在 pipe 方法的目的地時(shí),移除所有 data 事件的監(jiān)聽(tīng)函數(shù),并且調(diào)用 unpipe 方法,移除所有 pipe 方法的目的地
注意:只移除 data 事件的監(jiān)聽(tīng)函數(shù),并不會(huì)自動(dòng)引發(fā)數(shù)據(jù)流進(jìn)入「暫停態(tài)」。另外,存在 pipe 方法的目的地時(shí),調(diào)用 pause 方法,并不能保證數(shù)據(jù)流總是處于暫停態(tài),一旦那些目的地發(fā)出數(shù)據(jù)請(qǐng)求,數(shù)據(jù)流有可能會(huì)繼續(xù)提供數(shù)據(jù)。
用法
讀寫文件
var fs = require('fs'); // 新建可讀數(shù)據(jù)流 var rs = fs.createReadStream('./test1.txt'); // 新建可寫數(shù)據(jù)流 var ws = fs.createWriteStream('./test2.txt'); // 監(jiān)聽(tīng)可讀數(shù)據(jù)流結(jié)束事件 rs.on('end', function() { console.log('read text1.txt successfully!'); }); // 監(jiān)聽(tīng)可寫數(shù)據(jù)流結(jié)束事件 ws.on('finish', function() { console.log('write text2.txt successfully!'); }); // 把可讀數(shù)據(jù)流轉(zhuǎn)換成流動(dòng)態(tài),流進(jìn)可寫數(shù)據(jù)流中 rs.pipe(ws); 讀取 CSV 文件,并上傳數(shù)據(jù)(我在生產(chǎn)環(huán)境中寫過(guò)) var fs = require('fs'); var es = require('event-stream'); var csv = require('csv'); var parser = csv.parse(); var transformer = csv.transform(function(record) { return record.join(','); }); var data = fs.createReadStream('./demo.csv'); data .pipe(parser) .pipe(transformer) // 處理前一個(gè) stream 傳遞過(guò)來(lái)的數(shù)據(jù) .pipe(es.map(function(data, callback) { upload(data, function(err) { callback(err); }); })) // 相當(dāng)于監(jiān)聽(tīng)前一個(gè) stream 的 end 事件 .pipe(es.wait(function(err, body) { process.stdout.write('done!'); }));
更多用法
可以參考一下 https://github.com/jeresig/node-stream-playground ,進(jìn)去示例網(wǎng)站之后直接點(diǎn) add stream 就能看到結(jié)果了。
常見(jiàn)坑
用 rs.pipe(ws) 的方式來(lái)寫文件并不是把 rs 的內(nèi)容 append 到 ws 后面,而是直接用 rs 的內(nèi)容覆蓋 ws 原有的內(nèi)容
已結(jié)束/關(guān)閉的流不能重復(fù)使用,必須重新創(chuàng)建數(shù)據(jù)流
pipe 方法返回的是目標(biāo)數(shù)據(jù)流,如 a.pipe(b) 返回的是 b,因此監(jiān)聽(tīng)事件的時(shí)候請(qǐng)注意你監(jiān)聽(tīng)的對(duì)象是否正確
如果你要監(jiān)聽(tīng)多個(gè)數(shù)據(jù)流,同時(shí)你又使用了 pipe 方法來(lái)串聯(lián)數(shù)據(jù)流的話,你就要寫成:
data
.on('end', function() { console.log('data end'); }) .pipe(a) .on('end', function() { console.log('a end'); }) .pipe(b) .on('end', function() { console.log('b end'); });
常用類庫(kù)
event-stream 用起來(lái)有函數(shù)式編程的感覺(jué),個(gè)人比較喜歡
awesome-nodejs#streams 由于其他 stream 庫(kù)我都沒(méi)用過(guò),所以有需求的就直接看這里吧
以上內(nèi)容是小編給大家介紹的Node.js 中 Stream API 的使用,希望大家喜歡。
相關(guān)文章
nodejs和npm版本不匹配:ERROR:?npm?v9.5.1?is?known?not?to?run
本文主要介紹了nodejs和npm版本不匹配:ERROR:?npm?v9.5.1?is?known?not?to?run?on?Node.js,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06node.js中express中間件body-parser的介紹與用法詳解
這篇文章主要給大家介紹了關(guān)于node.js中express中間件body-parser的相關(guān)資料,文章通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-05-05node koa2實(shí)現(xiàn)上傳圖片并且同步上傳到七牛云存儲(chǔ)
這篇文章主要介紹了node koa2實(shí)現(xiàn)上傳圖片并且同步上傳到七牛云存儲(chǔ),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07node.js實(shí)現(xiàn)簡(jiǎn)單爬蟲(chóng)示例詳解
這篇文章主要為大家介紹了node.js實(shí)現(xiàn)簡(jiǎn)單爬蟲(chóng)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Node.js API詳解之 repl模塊用法實(shí)例分析
這篇文章主要介紹了Node.js API詳解之 repl模塊用法,結(jié)合實(shí)例形式分析了Node.js API中repl模塊基本功能、函數(shù)、使用方法及操作注意事項(xiàng),需要的朋友可以參考下2020-05-05Node.js中Sequelize?hook的使用方法小結(jié)
Sequelize?提供了多個(gè)?hook,用于在執(zhí)行數(shù)據(jù)庫(kù)操作時(shí)執(zhí)行一些自定義邏輯,本文為大家整理了一些常用的?Sequelize?hook?列表及其作用,希望對(duì)大家有所幫助2024-02-02