Node.js I/O流操作方法
I/O流,即輸入(input)、輸出(output)流,它提供了一種向存儲設(shè)備寫入數(shù)據(jù)和從存儲設(shè)備讀取數(shù)據(jù)的方式,它是Node.js中執(zhí)行讀寫文件操作的一種非常重要的方式。
1、流簡介
1.1、流的基本概念
程序中的流是一個抽象概念,當(dāng)程序需要從某個數(shù)據(jù)源讀取數(shù)據(jù)時,就會開啟一個數(shù)據(jù)流,數(shù)據(jù)源可以是文件、內(nèi)存或者網(wǎng)絡(luò)等,而當(dāng)程序?qū)?shù)據(jù)寫入某個數(shù)據(jù)源時,也會開啟一個數(shù)據(jù)流,而數(shù)據(jù)源的目的地也可以是文件、內(nèi)存或者網(wǎng)絡(luò)等。
以文件流為例,當(dāng)需要讀取一個文件時,如果使用fs模塊的readFile()方法讀取,程序會將該文件的內(nèi)容視為一個整體,為其分配緩存區(qū)并一次性將內(nèi)容讀取到緩存區(qū)中,在這期間,Node.js將不能執(zhí)行任何其他處理,這就可能導(dǎo)致一個問題,即如果文件很大,會耗費(fèi)較多的時間。
如果使用文件流讀取文件,則可以將文件一部分一部分地讀取,這樣可以保證效率,并且不會占用太大的內(nèi)存。
1.1.1、流的基本類型
Node.js中的流有4種基本類型,分別如下。
Readable:可讀流。Writable:可寫流。Duplex:可讀可寫流(也稱雙工流)?。Transform:轉(zhuǎn)換流,表示在讀寫過程中可以修改和變換數(shù)據(jù)的Duplex流。
1.1.2、流模塊的引用
使用Node.js中的流之前,首先要引用stream模塊,代碼如下:
const stream = require('stream');1.2、Buffer
Buffer就是開辟的一塊內(nèi)存區(qū)域,Buffer的大小就是開辟的內(nèi)存區(qū)域的大小。在流中,Buffer的大小取決于傳入流構(gòu)造函數(shù)的highWaterMark選項(xiàng)參數(shù),該參數(shù)指定了字節(jié)總數(shù)或者對象總數(shù),當(dāng)可讀緩沖區(qū)的總大小達(dá)到highWaterMark指定的閾值時,流會暫時停止從數(shù)據(jù)源讀取數(shù)據(jù),直到當(dāng)前緩沖區(qū)的數(shù)據(jù)被釋放;如果要獲取緩沖區(qū)中存儲的數(shù)據(jù),需要使用writable.writableBuffer或readable.readableBuffer。
2、可讀流的使用
2.1、流的讀取模式與狀態(tài)
2.1.1、可讀流的讀取模式
可讀流有兩種讀取模式,即流動(flowing)模式和暫停(paused)模式。
所有的可讀流都是以暫停模式開始,當(dāng)流處于暫停模式時,可以通過read()方法從流中按需讀取數(shù)據(jù)。
流動模式指的是一旦開始讀取文件,會按照highWaterMark的值按次讀取,直到讀取完為止。當(dāng)流處于流動模式時,因?yàn)閿?shù)據(jù)是持續(xù)變化的,所以需要使用監(jiān)聽事件來處理它。
流的暫停模式和流動模式是可以互相切換的,如通過添加data事件、使用resume()方法或者pipe()方法等都可以將可讀流從暫停模式切換為流動模式;使用paused()方法或者unpipe()方法可以將可讀流從流動模式切換為暫停模式。
2.1.2、可讀流的狀態(tài)
在實(shí)際使用可讀流時,它一共有3種狀態(tài),即初始狀態(tài)(null)、流動狀態(tài)(true)和非流動狀態(tài)(false)。
當(dāng)流處于初始狀態(tài)(null)時,由于沒有數(shù)據(jù)使用者,所以流不會產(chǎn)生數(shù)據(jù),這時如果監(jiān)聽data事件、調(diào)用pipe()方法或resume()方法,都會將當(dāng)前狀態(tài)切換為流動狀態(tài)(true),這樣可讀流即可開始主動地產(chǎn)生數(shù)據(jù)并觸發(fā)事件。
如果調(diào)用pause()方法或者unpipe()方法,就會將可讀流的狀態(tài)切換為非流動狀態(tài)(false),這將暫停流,但不會暫停數(shù)據(jù)生成。此時,如果再為data事件設(shè)置監(jiān)聽器,就不會再將狀態(tài)切換為流動狀態(tài)(true)了。
2.2、可讀流的創(chuàng)建
Node.js中的可讀流使用stream模塊中的Readable類表示,因此可以直接使用下面代碼創(chuàng)建:
const readable = new stream.Readable();
另外,還可以使用fs模塊的createReadStream方法創(chuàng)建可讀流,語法格式如下:
fs.createReadStream(path[, options])
path:讀取文件的文件路徑,可以是字符串、緩沖區(qū)或網(wǎng)址。options:讀取文件時的可選參數(shù),可選值如下。- flags:指定文件系統(tǒng)的權(quán)限,默認(rèn)為r。
- encoding:編碼方式,默認(rèn)為null。
- fd:文件描述符,默認(rèn)為null。
- mode:設(shè)置文件模式,默認(rèn)為0o666。
- autoClose:文件出錯或者結(jié)束時,是否自動關(guān)閉文件,默認(rèn)為true。
- emitClose:流銷毀時,是否發(fā)送close事件,默認(rèn)為true。
- start:指定開始讀取的位置。
- end:指定結(jié)束讀取的位置。
- highWaterMark:可讀取的閾值,一般設(shè)置在16~100KB范圍內(nèi)。
const fs = require("fs");
const read = fs.createReadStream("春夜喜雨.txt");
2.3、可讀流的屬性 方法及事件
可讀流提供了很多屬性、方法和事件,用來獲取可讀流信息、對可讀流進(jìn)行操作,以及監(jiān)聽可讀流的相應(yīng)操作,如下表:
常用屬性:
| 屬性 | 說明 |
|---|---|
destroyed | 可讀流是否已銷毀,如果已經(jīng)調(diào)用了readable.destroyO方法,則該屬性值為true |
readable | 可讀流是否被破壞、結(jié)束或者報錯 |
readableEncoding | 獲取可讀流的encoding屬性 |
readableEnded | 可讀流是否已經(jīng)沒有數(shù)據(jù),如果觸發(fā)了end事件,則該屬性值為rue |
readableFlowing | 可讀流的當(dāng)前狀態(tài) |
readableHighWaterMark | 構(gòu)造可讀流時傳入的high WaterMark的值 |
readableLength | 獲取準(zhǔn)備讀取的隊(duì)列中的字節(jié)數(shù)(或?qū)ο髷?shù)) |
常用方法:
| 方法 | 說明 |
|---|---|
read([sizel) | 從流中讀取數(shù)據(jù) |
setEncoding(encoding) | 設(shè)置從流中讀取的數(shù)據(jù)所使用的編碼 |
pause() | 暫停從可讀流對象發(fā)出的data事件 |
ispaused() | 獲取可讀流的當(dāng)前操作狀態(tài) |
destroy() | 銷毀可讀流 |
resume() | 恢復(fù)從可讀流對象發(fā)出的data事件 |
pipe(destination[,options]) | 把可讀流的輸出傳輸?shù)揭粋€由deatination參數(shù)指定的Writable流對象 |
unpipe([destinationl) | 分離附加的Writale流對象 |
filter(fn[,options]) | 篩選流 |
forEach(fn[,options]) | 迭代遍歷流 |
常用事件:
| 事件 | 說明 |
|---|---|
close | 當(dāng)流或其數(shù)據(jù)源被關(guān)閉時,觸發(fā)該事件 |
data | 在流將數(shù)據(jù)塊傳送給使用者后觸發(fā) |
end | 當(dāng)流中沒有數(shù)據(jù)可供使用時觸發(fā) |
error | 當(dāng)流由于底層內(nèi)部故障而無法生成數(shù)據(jù)或嘗試推送無效數(shù)據(jù)塊時觸發(fā) |
pause | 當(dāng)調(diào)用stream.pause().且readsFlowing不為false時觸發(fā) |
readable | 當(dāng)有數(shù)據(jù)可從流中讀取時觸發(fā) |
resume | 當(dāng)調(diào)用stream.resume().且readsFlowing不為true時觸發(fā) |
觸發(fā)流事件同樣采用on()方法實(shí)現(xiàn),例如,下面代碼創(chuàng)建了一個可讀流,然后觸發(fā)其data事件:
const fs = require("fs")
let read = fs.createReadStream('春夜喜雨.txt')
read.on('data', (chunk)=>{
console.log("讀取到的數(shù)據(jù):" + chunk.toString());
})
讀取到的數(shù)據(jù): 春夜喜雨
杜甫
好雨知時節(jié),當(dāng)春乃發(fā)生。
隨風(fēng)潛入夜,潤物細(xì)無聲。
野徑云俱黑,江船火獨(dú)明。
曉看紅濕處,花重錦官城。2.4、可讀流的常見操作
2.4.1、讀取數(shù)據(jù)
使用read()方法可以從流中讀取數(shù)據(jù),其語法格式如下:
對象名.read([size])
- size:要讀取的數(shù)據(jù)的字節(jié)數(shù)。
- 返回值:返回值可能為字符串、Buffer、null等。
const fs = require("fs");
const read = fs.createReadStream("春夜喜雨.txt", {encoding: 'utf-8'});
read.on('readable', ()=>{
while (null !== (chunk = read.read(25))) {
console.log(chunk.toString());
}
});
2.4.2、設(shè)置編碼格式
可讀流讀取數(shù)據(jù)時,默認(rèn)情況下沒有設(shè)置字符編碼,流數(shù)據(jù)返回的是Buffer對象。如果設(shè)置了字符編碼,則流數(shù)據(jù)返回指定編碼的字符串。設(shè)置可讀流中數(shù)據(jù)的編碼格式需要使用setEncoding()方法,其語法格式如下:
對象名.setEncoding(encoding)
const fs = require("fs");
const read = fs.createReadStream("春夜喜雨.txt");
read.setEncoding("utf8") //設(shè)置編碼格式
read.on('readable', function () {
while (null !== (chunk = read.read(25))) {
//設(shè)置編碼格式后,此處不再需要使用toString()方法
console.log(chunk);
}
}); 春夜喜雨
杜甫
好雨知時節(jié),當(dāng)春乃發(fā)生。
隨風(fēng)潛入夜,潤物細(xì)無聲。
野徑云俱黑,江船火獨(dú)明。2.4.3、 暫停與恢復(fù)流
pause()方法可以使流動模式的可讀流停止觸發(fā)data事件,并切換為非流動模式,其語法格式如下:
對象名.pause()
resume()方法可以恢復(fù)從可讀流對象發(fā)出的data事件,將可讀流切換為流動模式,其語法格式如下:
對象名.resume()
按行輸出故事內(nèi)容:
const fs = require('fs');
const read= fs.createReadStream("春夜喜雨.txt", {highWaterMark: 25});
read.setEncoding("utf8") //設(shè)置編碼格式
read.on('data', (chunk)=>{
console.log(chunk.toString());
read.pause();
setTimeout(()=>{
read.resume();
}, 1000);
});
read.on("close",()=>{
console.log("讀取完畢");
})
2.4.4、 獲取流的運(yùn)行狀態(tài)
對可讀流進(jìn)行操作時,可以使用ispaused()方法判斷流當(dāng)前的操作狀態(tài),該方法不需要參數(shù),返回結(jié)果為true或false,其語法格式如下:
對象名.ispaused()
const readable = new stream.Readable(); console.log(readable.ispaused()); readable.pause() console.log(readable.ispaused());
false true
ispaused()方法主要用于readable.pipe()底層的機(jī)制,大多數(shù)情況下不會直接使用該方法。
2.4.5、 銷毀流
使用destroy()方法可以銷毀可讀流,其語法格式如下:
對象名.destroy([error])
該方法中有一個可選參數(shù)error,用于在處理錯誤事件時發(fā)出錯誤。
const fs = require("fs")
const read = fs.createReadStream('春夜喜雨.txt')
read.setEncoding("utf8")
read.on('data', function (chunk) {
console.log("讀取到的數(shù)據(jù):\n" + chunk.toString());
})
read.destroy()
上面代碼運(yùn)行結(jié)果為空,因?yàn)樽詈笠恍写a銷毀了可讀流read,可讀流在銷毀后,會將讀取的數(shù)據(jù)清空,因此,通常在程序可能出現(xiàn)異常時,才會在處理異常的過程中使用銷毀流方法。
2.4.6、 綁定可寫流至可讀流
readable.pipe()方法可以將可寫流綁定到可讀流,并將可讀流自動切換到流動模式,同時將可讀流的所有數(shù)據(jù)推送到綁定的可寫流。pipe()方法的語法格式如下:
可讀流對象名.pipe(destination[, options])
destination:要綁定到可讀流的可寫流對象。options:保存管道選項(xiàng),通常為end參數(shù),其參數(shù)值為true,表示如果可讀流觸發(fā)end事件,可寫流也調(diào)用steam.end()結(jié)束寫入,如果設(shè)置end值為false,則目標(biāo)流就會保持打開。返回值:返回目標(biāo)可寫流。
pipe的含義為管道,比如要從A桶中向B桶中倒水,如果直接用A桶來倒水,那么水流可能會忽大忽小,而B桶中的水有可能因?yàn)橐绯龌蛘邅聿患笆褂枚速M(fèi),那么如何讓水不浪費(fèi)呢?這時就可以用一根水管連接A桶與B桶,這樣A桶中的水通過水管勻速地流向B桶,B桶中的水就可以及時使用而不會造成浪費(fèi)。流中的pipe也是如此,它是連接可讀流和可寫流的一條管道,可以實(shí)現(xiàn)讀取數(shù)據(jù)和寫入數(shù)據(jù)的一致性。這里需要說明的是,pipe()是可讀流的方法,只能將可寫流綁定到可讀流,反之則不可以。
通過將可寫流綁定至可讀流為文件追加內(nèi)容。
const fs = require("fs");
const read = fs.createReadStream('春夜喜雨.txt'); //創(chuàng)建可讀流
const write = fs.createWriteStream('aaa.txt', {flags: "a"}); //創(chuàng)建可寫流
read.pipe(write); //將可寫流綁定到可讀流
console.log("已完成")2.4.7、 解綁可寫流
上文講解了使用可讀流的pipe()方法可以將可寫流綁定到可讀流,還可以通過可讀流的unpipe()方法將已經(jīng)綁定的可寫流進(jìn)行解綁,其語法格式如下:
可讀流對象名.unpipe([destination])
該方法中有一個可選參數(shù)destination,表示要解綁的可寫流,如果該參數(shù)省略,表示解綁所有的可寫流。
const fs = require("fs");
const read = fs.createReadStream('春夜喜雨.txt'); //創(chuàng)建可讀流
const write = fs.createWriteStream('bbb.txt', {flags: "a"}); //創(chuàng)建可寫流
read.pipe(write); //將可寫流綁定到可讀流
console.log("已綁定可寫流")
read.unpipe(write) //解綁
console.log("已解綁可寫流")
上面代碼先為可讀流read綁定一個可寫流write,這時春夜喜雨.txt文件中的內(nèi)容都被追加到“bbb.txt”文件中,然后將write解綁,于是從“bbb.txt”文件中移除春夜喜雨.txt中的內(nèi)容,最終“涼州詞.txt”文件中的內(nèi)容將不會發(fā)生變化。
3、可寫流的使用
3.1、可寫流的創(chuàng)建
Node.js中的可寫流使用stream模塊中的Writable類表示,使用該類時,需要重寫其中的write()方法,因此要使用stream模塊中的Writable類創(chuàng)建可寫流,需要使用的代碼類似下面代碼:
const stream = require('stream');
const writable = new stream.Writable({
write: function (chunk, encoding, next) {
console.log(chunk.toString());
next();
}
});
另外,還可以使用fs模塊的createWriteStream()方法創(chuàng)建可寫流,語法格式如下:
fs.createWriteStream(path[, options])
path:寫入文件的文件路徑。options:寫入文件時的可選參數(shù),可選值如下。- flags:指定文件系統(tǒng)的權(quán)限,默認(rèn)為w,如果要修改文件內(nèi)容,而不是替換,需要將該值設(shè)置為a。
- encoding:編碼方式,默認(rèn)為null。
- fd:文件描述符,默認(rèn)為null。
- mode:設(shè)置文件模式,默認(rèn)為0o666。
- autoClose:文件出錯或者結(jié)束時,是否自動關(guān)閉文件,默認(rèn)為true。
- emitClose:流銷毀時,是否發(fā)送close事件,默認(rèn)為true。
- start:指定開始寫入的位置。
- highWaterMark:可寫入的閾值,一般設(shè)置在16~100KB范圍內(nèi)。
var fs = require("fs");
var write = fs.createWriteStream('demo.txt'); //創(chuàng)建可寫流
3.2、可寫流的屬性 方法及事件
可寫流提供了很多屬性、方法和事件,用來獲取可寫流信息、對可寫流進(jìn)行操作,以及監(jiān)聽可寫流的相應(yīng)操作,它們的說明分別如表所示。
常用屬性:
| 屬性 | 說明 |
|---|---|
destroyed | 可寫流是否已銷毀,如果已經(jīng)調(diào)用了writable.destroy(),則為true |
writable | 可寫流是否被破壞、報錯或結(jié)束 |
writableEnded | 可寫流是否已經(jīng)沒有數(shù)據(jù),如果在調(diào)用writable.end()之后,該值為rue |
writableCorked | 獲取完全uncork流需要調(diào)用writable.uncork()的次數(shù) |
writableFinished | 可寫流中的數(shù)據(jù)是否已傳輸完,在觸發(fā)finish事件之前需將其設(shè)置為true |
writableHigh WaterMark | 返回構(gòu)造可寫流時傳入的high WaterMark的值 |
writableLength | 包含準(zhǔn)備寫入的隊(duì)列中的字節(jié)數(shù)(或?qū)ο螅?/td> |
writableNeedDrain | 如果流的緩沖區(qū)已滿且流將發(fā)出drain,則為true |
常用方法:
| 方法 | 說明 |
|---|---|
write() | 寫入數(shù)據(jù) |
end() | 通知可寫流對象寫入結(jié)束 |
setDefaultEncoding | 為可寫流設(shè)置默認(rèn)的編碼方式 |
end() | 關(guān)閉可寫流 |
destroy() | 銷毀可寫流 |
cork() | 強(qiáng)制把所有寫入的數(shù)據(jù)都緩沖到內(nèi)存中 |
uncork() | 將調(diào)用stream.cork0方法緩沖的所有數(shù)據(jù)輸出到目標(biāo) |
常用事件:
| 事件 | 說明 |
|---|---|
close | 當(dāng)可寫流或數(shù)據(jù)源被關(guān)閉時,觸發(fā)該事件 |
open | 創(chuàng)建可寫流的同時會打開文件,而打開文件就會觸發(fā)該事件 |
drain | 當(dāng)寫入緩沖區(qū)為空時觸發(fā)該事件 |
error | 寫入或管道數(shù)據(jù)發(fā)生錯誤時觸發(fā)該事件 |
finish | 調(diào)用stream.end()且緩沖區(qū)數(shù)據(jù)都已傳給底層系統(tǒng)之后觸發(fā)該事件 |
pipe | 當(dāng)在可讀流上調(diào)用stream.pipe()方法時會觸發(fā)該事件,并將此可寫流添加到其目標(biāo)集 |
unpipe | 在可讀流上調(diào)用stream.unpipeO方法時會觸發(fā)該事件,從其目標(biāo)集中移除此可寫流 |
3.3、可寫流的常見操作
3.3.1、寫入數(shù)據(jù)
使用write()方法可以向流中寫入數(shù)據(jù),其語法格式如下:
對象名.write( chunk[, encoding, callback])
- chunk:要寫入的數(shù)據(jù),其值可以是字符串、緩沖區(qū)或數(shù)組等。
- encoding:可選參數(shù),表示寫入數(shù)據(jù)時的編碼方式。
- callback:可選參數(shù),是一個回調(diào)函數(shù),寫入數(shù)據(jù)完成后執(zhí)行。
const fs = require("fs")
let txt = "這首詩抓住了邊塞風(fēng)光景物的一些特點(diǎn),借其嚴(yán)寒春遲及胡笳聲聲來寫戰(zhàn)士們的心理活動,反映了邊關(guān)將士的生活狀"
+ "況。詩風(fēng)蒼涼悲壯,但并不低沉,以俠骨柔情為壯士之聲,這仍然是盛唐氣象的回響。"
//在文件原有內(nèi)容后面追加內(nèi)容,所以定義文件權(quán)限為“a”
let decr = fs.createWriteStream("涼州詞.txt", {flags: "a"})
decr.write("\n鑒賞:\n" + txt, "utf8") //寫入內(nèi)容
3.3.2、設(shè)置編碼方式
使用setDefaultEncoding()方法可以設(shè)置可寫流的默認(rèn)編碼方式,其語法格式如下:
對象名.setDefaultEncoding(encoding)
- 參數(shù)encoding表示要設(shè)置的編碼方式。
const fs = require("fs")
const writeSteam = fs.createWriteStream("demo.txt")
writeSteam.setDefaultEncoding("utf8") //設(shè)置編碼方式
writeSteam.write("測試數(shù)據(jù)") //寫入內(nèi)容
3.3.3、關(guān)閉流
寫入流的end()方法用來標(biāo)識已經(jīng)沒有需要寫入流中的數(shù)據(jù)了,因此通常用來關(guān)閉流,其語法格式如下:
對象名.end([chunk[, encoding]][, callback])
- chunk:可選參數(shù),表示關(guān)閉流之前要寫入的數(shù)據(jù)。
- encoding:如果chunk為字符串,那么encoding為編碼方式。
- callback:流結(jié)束或者報錯時的回調(diào)函數(shù)。
const fs = require("fs")
const writeSteam = fs.createWriteStream("demo.txt")
writeSteam.setDefaultEncoding("utf8") //設(shè)置編碼方式
writeSteam.write("測試數(shù)據(jù)") //寫入內(nèi)容
writeSteam.end("寫入完成") //關(guān)閉流
//writeSteam.write('繼續(xù)寫入');
測試數(shù)據(jù) 寫入完成
使用end()方法關(guān)閉流后,無法再向流中寫入數(shù)據(jù),否則將會產(chǎn)生異常,例如,去掉上面代碼中最后一行的注釋,再次運(yùn)行時,將會出現(xiàn)錯誤提示。
3.3.4、銷毀流
使用destroy()方法可以銷毀所創(chuàng)建的寫入流,并且流被銷毀后,無法再向流寫入數(shù)據(jù)。其語法格式如下:
對象名.destroy([error])
- 參數(shù)error為可選參數(shù),表示使用error事件觸發(fā)的錯誤。
const fs = require("fs")
const writeSteam = fs.createWriteStream("demo.txt")
writeSteam.setDefaultEncoding("utf8") //設(shè)置編碼方式
writeSteam.write("測試數(shù)據(jù)") //寫入內(nèi)容
writeSteam.destroy() //銷毀流
上面代碼運(yùn)行后,將會導(dǎo)致demo.txt文件中沒有任何數(shù)據(jù),因?yàn)殡m然第4行代碼中使用write()方法寫入了數(shù)據(jù),但由于緊接著銷毀了寫入流,這將導(dǎo)致使用該流執(zhí)行的任何操作都會失效。因此,在使用寫入流銷毀操作時,通常在異常處理中使用該操作。
3.3.5、將數(shù)據(jù)緩沖到內(nèi)存
使用寫入流的cork()方法可以強(qiáng)制把所有寫入的數(shù)據(jù)都緩沖到內(nèi)存中,它的主要目的是為了適應(yīng)將幾個數(shù)據(jù)快速連續(xù)地寫入流的情況。cork()方法不會立即將它們轉(zhuǎn)發(fā)到底層目標(biāo)處,而是緩沖所有數(shù)據(jù)塊,直到調(diào)用uncork()方法。cork()方法的語法格式如下:
對象名.cork()
當(dāng)使用uncork()方法或end()方法時,緩沖區(qū)數(shù)據(jù)將被刷新。
const stream = require('stream');
const writable = new stream.Writable({
write: function (chunk, encoding, next) {
console.log(chunk.toString());
next();
}
});
writable.write('天氣晴朗');
writable.cork();
writable.write('陽光明媚');
天氣晴朗
通過觀察上面結(jié)果,發(fā)現(xiàn)調(diào)用cork()方法后,接下來要輸出的內(nèi)容并沒有顯示。
3.3.6、輸出緩沖后的數(shù)據(jù)
前面介紹了cork()方法,用以強(qiáng)制把所有寫入的數(shù)據(jù)都緩沖到內(nèi)存中,而使用uncork()方法可以將調(diào)用cork()方法后緩沖的所有數(shù)據(jù)輸出到目標(biāo)處。uncork()方法的語法格式如下:
對象名.uncork()
const stream = require('stream');
const writable = new stream.Writable({
write: function (chunk, encoding, next) {
console.log(chunk.toString());
next();
}
});
writable.write('天氣晴朗');
writable.cork();
writable.write('陽光明媚');
writable.uncork();
天氣晴朗 陽光明媚
4、雙工流與轉(zhuǎn)換流
4.1、雙工流
雙工流Duplex可以實(shí)現(xiàn)流的可讀和可寫功能,即同時實(shí)現(xiàn)Readable和Writable。實(shí)現(xiàn)雙工流需要進(jìn)行以下3步。
- 繼承Duplex類。
- 實(shí)現(xiàn)_read()方法。
- 實(shí)現(xiàn)_write()方法。
const Duplex = require('stream').Duplex;
const myDuplex = new Duplex({
_read(size) {
// ...
},
_write(chunk, encoding, callback) {
// ...
}
});
示例:雙工流的使用
const stream = require('stream');
const duplexStream = stream.Duplex();
duplexStream._read = function () {
this.push('讀取數(shù)據(jù)');
this.push(null)
}
duplexStream._write = function (data, enc, next) {
console.log(data.toString());
next();
}
duplexStream.on('data', data => console.log("監(jiān)聽:" + data.toString()));
duplexStream.on('end', data => console.log('監(jiān)聽:' + '讀取完成'));
duplexStream.write('寫入數(shù)據(jù)');
duplexStream.end();
duplexStream.on('finish', data => console.log('監(jiān)聽:' + '寫入完成'));
寫入數(shù)據(jù) 監(jiān)聽:讀取數(shù)據(jù) 監(jiān)聽:寫入完成 監(jiān)聽:讀取完成
4.2、轉(zhuǎn)換流
轉(zhuǎn)換流Transform其實(shí)也是雙工流,它與Duplex的區(qū)別在于,Duplex雖然同時具備可讀流和可寫流的功能,但兩者是相對獨(dú)立的,而Transform中可讀流的數(shù)據(jù)會經(jīng)過一定的處理過程自動進(jìn)入可寫流。需要說明的是,從可讀流到可寫流,它們的數(shù)據(jù)量不一定相同。例如,常見的壓縮、解壓縮用的zlib就使用了轉(zhuǎn)換流,壓縮和解壓縮前后的數(shù)據(jù)量明顯不同。
實(shí)現(xiàn)轉(zhuǎn)換流需要進(jìn)行以下兩步:
- 繼承Transform類。
- 實(shí)現(xiàn)_transform()方法。_transform()方法用來接收數(shù)據(jù),并產(chǎn)生輸出(需要調(diào)用this.push(data),如果不調(diào)用,則接收數(shù)據(jù)但不輸出)?。當(dāng)數(shù)據(jù)處理完后,必須執(zhí)行callback(err, data)回調(diào)函數(shù),該函數(shù)中的第一個參數(shù)用于傳遞錯誤信息,第二個參數(shù)用來輸出數(shù)據(jù)(效果和this.push(data)相同)?,但參數(shù)可以省略。
const Stream = require('stream')
class TransformReverse extends stream.Transform {
constructor() { //繼承構(gòu)造函數(shù)
super()
}
_transform(data, encoding, callback) {
this.push(data);
callback();
}
}
示例:轉(zhuǎn)換流的使用
const Stream = require('stream');
class TransformStream extends Stream.Transform {
constructor() {
super()
}
_transform(data, encoding, callback) {
//將寫入的數(shù)據(jù)進(jìn)行反轉(zhuǎn)
const res = data.toString().split('').reverse().join('');
this.push(res) //輸出反轉(zhuǎn)后的數(shù)據(jù)
callback()
}
}
var transformStream = new TransformStream();
transformStream.on('data', data => console.log(data.toString()))
transformStream.on('end', data => console.log('讀取完成'));
transformStream.write('寫入數(shù)據(jù)');
transformStream.end()
transformStream.on('finish', data => console.log('寫入完成'));
據(jù)數(shù)入寫 讀取完成 寫入完成
到此這篇關(guān)于Node.js I/O流操作方法的文章就介紹到這了,更多相關(guān)Node.js I/O流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Node.js中的process.nextTick使用實(shí)例
這篇文章主要介紹了Node.js中的process.nextTick使用實(shí)例,nextTick函數(shù)有什么用、怎么用、和setTimeout有什么區(qū)別呢,本文就講解了這些知識,需要的朋友可以參考下2015-06-06
express.js如何做mysql注入與node-mysql中防止SQL注入方法解析
這篇文章主要介紹了express.js如何做mysql注入與node-mysql中防止SQL注入方法,結(jié)合實(shí)例形式分析了express框架使用mysql數(shù)據(jù)庫過程中SQL注入的原理與防范技巧,需要的朋友可以參考下2023-05-05
Nodejs中解決cluster模塊的多進(jìn)程如何共享數(shù)據(jù)問題
本篇文章主要介紹了Nodejs中解決cluster模塊的多進(jìn)程如何共享數(shù)據(jù)問題,有需要的可以了解一下。2016-11-11
Node.js實(shí)現(xiàn)簡單管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Node.js實(shí)現(xiàn)簡單管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-09-09

