node基础(4)----Stream

流的概念


  1. 流是一组有序的,有起点和终点的字节数据传输手段。
  2. 它不关心文件的整体内容,只关注是否从文件中读到了数据,以及读到数据之后的处理。
  3. 流是一个抽象接口,被 Node 中的很多对象所实现。比如HTTP 服务器request和response对象都是流。

那么说了这些,到底有哪些我们常用的流呢?下面我就一一介绍:

createReadStream

我们首先看的是可读流,它的实现机制是在默认情况下不会将内容输出,而是在内部先创建一个buffer,先读取highWaterMark长度。可读流有5个事件,分别是`data`,`close`,`open`,`end`,`error`,可读流会疯狂的触发data事件,直到读完为止,除此之外,createReadStream还有两个事件,暂停(pause)和继续(resume),让我们来看一下用法:
let fs = require('fs');
let path = require('path');

let re = fs.createReadStream(path.join(__dirname,'1.txt'),{
    flags:'r', // 文件的操作是读取操作
    encoding:'utf8', // 默认是null null代表的是buffer
    autoClose:true, // 读取完毕后自动关闭
    highWaterMark:3,// 默认是64k  64*1024b    start:0,//start和end是包前又包后,共10个字节
    end:9
})
re.on('open',function(){
    console.log('文件打开了')

})
re.on('close',function(){
    console.log('关闭')
})
//这里采用发布订阅模式,内部有一个newLisenter函数来监听实例上绑了哪些事件。而内部的事件触发采用的是emit()
re.on('error',function(err){
    console.log(err)
})
re.on('data',function(data){//触发data就会一直流
    console.log(data)
    re.pause()//暂停
})
setInterval(function(){
    re.resume()//继续
},3000)
re.on('end',function(){
    console.log('end')
})
通过这几个方法一实验就知道内部是怎么实现的了,我这里用vscode跑了一下:

这里写图片描述


createWriteStream

可写流相对来说用法就比较简单了,只有write,end,两个方法。需要注意的是写入的数据必须是字符串或者buffer。我们来看一下它的用法:

let fs = require('fs');

let ws = fs.createWriteStream('./1.txt',{
    flags:'w',
    mode:0o666,
    autoClose:true,
    highWaterMark:3, // 默认写是16k
    encoding:'utf8',
    start:0
});
// 写入的数据必须是字符串或者buffer
// flag代表是否能继续写
// 表示符表示的并不是是否写入 表示的是能否继续写,但是返回false 也不会丢失,就是会把内容放到内存中
let flag = ws.write(1+'','utf8',()=>{}); // 异步的方法
console.log(flag);
flag = ws.write(1+'','utf8',()=>{}); // 异步的方法
console.log(flag);
// flag = ws.write(1+'','utf8',()=>{}); // 异步的方法
// console.log(flag);

//ws.end('ok'); // 当写完后 就不能再继续写了
//ws.write('123'); //write after end

ws还有一个方法就是drain,抽干方法 当都写入完后会触发drain事件, 必须缓存区满了 ,满了后被清空了才会出发drain

let flag = ws.write(1+'','utf8',()=>{}); // 异步的方法
console.log(flag);
flag = ws.write(1+'','utf8',()=>{}); // 异步的方法
console.log(flag);
flag = ws.write(1+'','utf8',()=>{}); // 异步的方法
console.log(flag);
ws.on('drain',function(){
    console.log('drain')
});

这个drain以后可以让可写流配合可读流写一个pipe方法。
pipe是什么呢? 其实就是一个读一点写一点的方法,实现的是下面的这样的事:

let fs = require('fs');
let path = require('path');

let rs = fs.createReadStream(path.join(__dirname,'./1.txt'),{
    highWaterMark:3
})
let ws = fs.createWriteStream(path.join(__dirname,'./2.txt'),{
    highWaterMark:1
})

rs.on('data',function(chunk){
let flags = ws.write(chunk);
if(!flags){
    rs.pause()
}
})
ws.on('drain',function(){
    console.log('干了')
    rs.resume()
})

而pipe 的实现方法就简单的让人感动:

rs.pipe(ws); 

readable

readable也是一个很实用的可读流的方法,它可以很智能的的实现暂停流。
readable的实现原理是:

当我只要创建一个流 就会先把缓存区 填满,等待着你自己消费
如果当前缓存区被清空后会再次触发readable事件
当你消费小于 最高水位线时 会自动添加highWater这么多数据

let fs = require('fs');
let path = require('path');
let rs = fs.createReadStream(path.join(__dirname,'1.txt'),{
    highWaterMark:3
});
// rs.on('data'); 正常情况下我们会直接用data,但是这个方法会一直读
rs.on('readable',function(){
    let result = rs.read(1);
    console.log(result); //当我们读一个时,这个时候输出的就是一个
     result = rs.read(1);
    console.log(result);//当我们读两个时,这个时候输出的就是俩个
      result = rs.read(1);
    console.log(result);//当我们读到总共缓存区的个数时,这个时候就读完就接着触发readable事件,
});

执行结果是
这里写图片描述

这个readable还有一个特别智能的操作,当读一个或没读到highWaterMark的值时,并且半天没操作,程序会自动给你‘续杯‘。

let fs = require('fs');
let path = require('path');
let rs = fs.createReadStream(path.join(__dirname,'1.txt'),{
    highWaterMark:3
});

rs.on('readable',function(){
    let result = rs.read(1);
    console.log(rs._readableState.length); // 缓存区的个数
    setTimeout(function(){
        console.log(rs._readableState.length);
    },1000)
});

结果如下
这里写图片描述

我想读5个 缓存区只有3个 他会更改缓存区的大小再去读取


Duplex

双工流,读的时候要以this.push(null)结尾,写的时候要以 callback();结尾。

let {Duplex} =  require('stream');
// 双工流 又能读 又能写,而且读取可以没关系(互不干扰);
let d = Duplex({
    read(){
        this.push('hello');
        this.push(null)
    },
    write(chunk,encoding,callback){
        console.log(chunk);
        callback();
    }
});
d.on('data',function(data){
    console.log(data);
});
d.write('hello');

Transform

转换流,就是Duplex 他不需要实现read write。

let {Transform} =  require('stream');

// 他的参数和可写流一样
let tranform1 = Transform({
    transform(chunk,encoding,callback){
        this.push(chunk.toString().toUpperCase()); // 将输入的内容放入到可读流中
        callback();
    }
});
let tranform2 = Transform({
    transform(chunk,encoding,callback){
        console.log(chunk.toString());
        callback();
    }
});
// 等待你的输入
// rs.pipe(ws);
// 希望将输入的内容转化成大写在输出出来
process.stdin.pipe(tranform1).pipe(tranform2);

这个写法是为了让输入转化成大写输出,process.stdin(标准输入)可视为可读流。
最后用node 运行一下这个文件,输入小写字母就会转化成大写字母。

可读流里只能放buffer或者字符串 对象流里可以放对象

自定义一个流

let {Readable} = require('stream');

// 想实现什么流 就继承这个流
// Readable里面有一个read()方法,默认掉_read()
// Readable中提供了一个push方法你调用push方法就会触发data事件
let index = 9;
class MyRead extends Readable{
    _read(){
        // 可读流什么时候停止呢? 当push null的时候停止
        if(index-->0)return this.push('123');
        this.push(null);
    }
}
let mr = new MyRead;
mr.on('data',function(data){
    console.log(data);
});
node-stream-zip 是查看和提取大型 ZIP 文件的 Node.js 库。特性:从不加载完整的归档到内存,一切都是通过块读取大型归档支持所有操作都是非阻塞,非同步 i/o快速初始化无依赖,无二进制组件内置 zlib 模块解压deflate, deflate64, sfx, macosx/windows 内置归档ZIP64 支持安装$ npm install node-stream-zip使用var StreamZip = require('node-stream-zip');   var zip = new StreamZip({       file: 'archive.zip',       storeEntries: true     }); zip.on('error', function(err) { /*handle*/ }); zip.on('ready', function() {     console.log('Entries read: '   zip.entriesCount);     // stream to stdout     zip.stream('node/benchmark/net/tcp-raw-c2s.js', function(err, stm) {         stm.pipe(process.stdout);     });     // extract file     zip.extract('node/benchmark/net/tcp-raw-c2s.js', './temp/', function(err) {         console.log('Entry extracted');     });     // extract folder     zip.extract('node/benchmark/', './temp/', function(err, count) {         console.log('Extracted '   count   ' entries');     });     // extract all     zip.extract(null, './temp/', function(err, count) {         console.log('Extracted '   count   ' entries');     });     // read file as buffer in sync way     var data = zip.entryDataSync('README.md'); }); zip.on('extract', function(entry, file) {     console.log('Extracted '   entry.name   ' to '   file); }); zip.on('entry', function(entry) {     // called on load, when entry description has been read     // you can already stream this entry, without waiting until all entry descriptions are read (suitable for very large archives)      console.log('Read entry ', entry.name); });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值