node.js中的流是一種資料傳輸手段,流是有順序的。流不關心整體流程,只管取出資料,獲取資料後的操作。
流有四種基本的型別
Readable - 可讀的流 (例如 fs.createReadStream()).
Writable - 可寫的流 (例如 fs.createWriteStream()).
Duplex - 可讀寫的流 (例如 net.Socket).
Transform - 在讀寫過程中可以修改和變換資料的 Duplex 流 (例如 zlib.createDeflate()).
可讀流有兩種模式
flowing 流動模式
paused 暫停模式
流動模式:flowing 沒有快取區。讀一點資料,發射一點資料。當資料全部讀完了觸發一個end事件。例如:pipe(),resume()方法不走快取.
data事件,當你一旦開始監聽data事件的時候,流就可以讀檔案的內容並且發射data 。預設請況下,當你監聽data事件之後,會不停的讀資料,然後觸發data事件,觸發完data事件後再次讀資料。
let rs=fs.createReadStream('./11.txt',{
highWaterMark:3
});
rs.setEncoding('utf8');rs.on('data',function(data){
//data獲取到的是個buffer,要想獲取字元需要設定編碼
console.log(data);
});
rs.on('end',function(){
console.log('檔案讀完了');
});複製程式碼
pipe是可讀流 的方法
ReadStream.prototype.pipe = function (dest) {
this.on('data', (data)=>{
let flag = dest.write(data);//寫入資料,返回true,說明快取區沒滿還可以繼續寫。返回
false暫停一下。監聽drain事件,等到觸發drain事件說明資料消化完了,再繼續讀取資料
if(!flag){
this.pause();
}
});
dest.on('drain', ()=>{
this.resume();
});
this.on('end', ()=>{
dest.end();
});
}
ReadStream.prototype.pause = function(){
this.flowing = false;
}
ReadStream.prototype.resume = function(){
this.flowing = true;
this.read();
}
複製程式碼
dest 資料寫入目標 可以在單個可讀流上繫結多個可寫流。 複製程式碼
const r = fs.createReadStream('file.txt');
const z = zlib.createGzip();
const w = fs.createWriteStream('file.txt.gz');
r.pipe(z).pipe(w);複製程式碼
暫停模式:paused (初始化模式) 內部設定一個快取區,快取區預設大小64kb.實際大小以highWaterMark的值為準。當你監聽 readable事件的時候,會進入暫停模式。讀取highWaterMark的值放入快取區,觸發readable事件。
let fs = require('fs');
let rs = fs.createReadStream('./1.txt',{
highWaterMark:3
});
rs.on('readable',()=>{
onsole.log(rs._readableState.length);//3
let ch = rs.read(1);
//當你讀了一個位元組後,發現只剩下2個位元組,不夠highWaterMark,會再次讀取highWaterMark個位元組並填到
快取區內
console.log(rs._readableState.length);//2
let ch = rs.read(3);
setTimeout(()=>{
console.log(rs._readableState.length);//5
},200)
});
複製程式碼
可寫流就是往裡面寫資料
當你往可寫流裡寫資料的時候,不是會立刻寫入檔案的,而是會先寫入快取區,快取區的大小就是highWaterMark的值,預設是16k。
然後等快取區滿了之後再次真正的寫入檔案裡。
let fs=require('fs');
let ws=fs.createWriteStream('22.txt',{
flags:'w',
mode:0o666,
start:0,
highWaterMark:3
});
//如果快取區已滿,返回false.如果快取區沒滿返回true.
//如果能接著寫,返回true.如果不能寫返回false.
//按理說如果返回了false,就不能再往裡面寫了。但是如果繼續往裡面寫,也不會丟失,會快取在記憶體裡。
等快取區清空之後再從記憶體裡讀出來let flag=ws.write('1');
console.log(flag);//true
flag=ws.write('2');
console.log(flag);//true
flag=ws.write('3');
console.log(flag);//false複製程式碼
自定義流
let {Writable,Readable,Duplex,Transform} = require('stream');複製程式碼
自定義可讀流
為了實現可讀流,引用Readable介面並用它構造新物件。
- 我們可以直接把供使用的資料push出去。
- 當push一個null物件就意味著我們想發出訊號——這個流沒有更多資料了。
var stream = require('stream');
var util = require('util');
util.inherits(Counter, stream.Readable);
function Counter(options) {
stream.Readable.call(this, options);
this._index = 0;
}
Counter.prototype._read = function() {
if(this._index++<3){
this.push(this._index+'');
}else{
this.push(null);
}
};
var counter = new Counter();
counter.on('data', function(data){
console.log("讀到資料: " + data.toString());//no maybe
});
counter.on('end', function(data){
console.log("讀完了");
});
複製程式碼
自定義可寫流
為了實現可寫流,我們需要使用流模組中的Writable建構函式。 我們只需給Writable建構函式傳遞一些選項並建立一個物件。唯一需要的選項是write函式,該函式揭露資料塊要往哪裡寫。
- chunk通常是一個buffer,除非我們配置不同的流。
- encoding是在特定情況下需要的引數,通常我們可以忽略它。
- callback是在完成處理資料塊後需要呼叫的函式。這是寫資料成功與否的標誌。若要發出故障訊號,請用錯誤物件呼叫回撥函式
var stream = require('stream');
var util = require('util');
util.inherits(Writer, stream.Writable);
let stock = [];
function Writer(opt) {
stream.Writable.call(this, opt);
}
Writer.prototype._write = function(chunk, encoding, callback) {
setTimeout(()=>{
stock.push(chunk.toString('utf8'));
console.log("增加: " + chunk);
callback();
},500)
};
var w = new Writer();
for (var i=1; i<=5; i++){
w.write("專案:" + i, 'utf8');
}
w.end("結束寫入",function(){
console.log(stock);
});
複製程式碼
雙工流
雙工流(可讀可寫流)是可讀流和可寫流的實現。例如:net.Socket
let {Duplex} = require('stream');
let index = 0;
let s = Duplex({
read(){
if(index++<3)
this.push('a');
else
this.push(null);
},
write(chunk,encoding,cb){
console.log(chunk.toString().toUpperCase());
cb();
}
});
//process.stdin 標準輸入流
//proces.stdout標準輸出流
process.stdin.pipe(s).pipe(process.stdout);複製程式碼
Transform轉換流
轉換流是實現資料轉換的,(可讀流和可寫流)只能實現一種。
let {Transform} = require('stream');
let t = Transform({
transform(chunk,encoding,cb){
this.push(chunk.toString().toUpperCase());
cb();
}
});
process.stdin.pipe(t).pipe(process.stdout);
複製程式碼