深入nodejs中流(stream)的理解

流的基本概念及理解

流是一种数据传输手段,是有顺序的,有起点和终点,比如你要把数据从一个地方传到另外一个地方

流非常重要,gulp,webpack,HTTP里的请求和响应,http里的socket都是流,包括后面压缩,加密等

流为什么这么好用还这么重要呢?

  • 因为有时候我们不关心文件的主体内容,只关心能不能取到数据,取到数据之后怎么进行处理
  • 对于小型的文本文件,我们可以把文件内容全部读入内存,然后再写入文件,比如grunt-file-copy
  • 对于体积较大的二进制文件,比如音频、视频文件,动辄几个GB大小,如果使用这种方法,很容易使内存“爆仓”。
  • 理想的方法应该是读一部分,写一部分,不管文件有多大,只要时间允许,总会处理完成,这里就需要用到流的概念

流是一个抽象接口,被Node中很多对象所实现,比如HTTP服务器request和response对象都是流

Node.js 中有四种基本的流类型:

  • Readable - 可读的流 (例如 fs.createReadStream()).
  • Writable - 可写的流 (例如 fs.createWriteStream()).
  • Duplex - 可读写的流 (例如 net.Socket).
  • Transform - 在读写过程中可以修改和变换数据的 Duplex 流 (例如 zlib.createDeflate()).

可以通过 require(‘stream’) 加载 Stream 基类。其中包括了 Readable 流、Writable 流、Duplex 流和 Transform 流的基类

Readable streams可读流

可读流(Readable streams)是对提供数据的 源头(source)的抽象
可读流的例子包括:

  • HTTP responses, on the client :客户端请求
  • HTTP requests, on the server :服务端请求
  • fs read streams :读文件
  • zlib streams :压缩
  • crypto streams :加密
  • TCP sockets :TCP协议
  • child process stdout and stderr :子进程标准输出和错误输出
  • process.stdin :标准输入

所有的 Readable 都实现了 stream.Readable 类定义的接口

通过流读取数据

  • 用Readable创建对象readable后,便得到了一个可读流
  • 如果实现_read方法,就将流连接到一个底层数据源
  • 流通过调用_read向底层请求数据,底层再调用流的push方法将需要的数据传递过来
  • 当readable连接了数据源后,下游便可以调用readable.read(n)向流请求数据,同时监听readable的data事件来接收取到的数据

下面简单举个可读流的例子:

  • 监听可读流的data事件,当你一旦开始监听data事件的时候,流就可以读文件的内容并且发射data,读一点发射一点读一点发射一点
  • 默认情况下,当你监听data事件之后,会不停的读数据,然后触发data事件,触发完data事件后再次读数据
  • 读的时候不是把文件整体内容读出来再发射出来的,而且设置一个缓冲区,大小默认是64K,比如文件是128K,先读64K发射出来,再读64K在发射出来,会发射两次
  • 缓冲区的大小可以通过highWaterMark来设置
let fs = require('fs');
//通过创建一个可读流
let rs = fs.createReadStream('./1.txt',{
    flags:'r',//我们要对文件进行何种操作
    mode:0o666,//权限位
    encoding:'utf8',//不传默认为buffer,显示为字符串
    start:3,//从索引为3的位置开始读
    //这是我的见过唯一一个包括结束索引的
    end:8,//读到索引为8结束
    highWaterMark:3//缓冲区大小
});
rs.on('open',function () {
   
    console.log('文件打开');
});
rs.setEncoding('utf8');//显示为字符串
//希望流有一个暂停和恢复触发的机制
rs.on('data',function (data) {
   
    console.log(data);
    rs.pause();//暂停读取和发射data事件
    setTimeout(function(){
   
        rs.resume();//恢复读取并触发data事件
    },2000);
});
//如果读取文件出错了,会触发error事件
rs.on('error',function () {
   
    console.log("error");
});
//如果文件的内容读完了,会触发end事件
rs.on('end',function () {
   
    console.log('读完了');
});
rs.on('close',function () {
   
    console.log('文件关闭');
});

/**
文件打开
334
455
读完了
文件关闭
**/
可读流的简单实现
let fs = require('fs');
let ReadStream = require('./ReadStream');
let rs = ReadStream('./1.txt', {
    flags: 'r',
    encoding: 'utf8',
    start: 3,
    end: 7,
    highWaterMark: 3
});
rs.on('open', function () {
   
    console.log("open");
});
rs.on('data', function (data) {
   
    console.log(data);
});
rs.on('end', function () {
   
    console.log("end");
});
rs.on('close', function () {
   
    console.log("close");
});
/**
 open
 456
 789
 end
 close
 **/
let fs = require('fs');
let EventEmitter = require('events');

class ReadStream extends EventEmitter {
    constructor(path, options) {
        super(path, options);
        this.path = path;
        this.highWaterMark = options.highWaterMark || 64 * 1024;
        this.buffer = Buffer.alloc(this.highWaterMark);
        this.flags = options.flags || 'r';
        this.encoding = options.encoding;
        this.mode = options.mode || 0o666;
        this.start = options.start || 0;
        this.end = options.end;
        this.pos = this.start;
        this.autoClose = options.autoClose || true;
        this.bytesRead = 0;
        this.closed = false;
        this.flowing;
        this.needReadable = false;
        this.length = 0;
        this.buffers = [];
        this.on('end', function () {
   
            if (this.autoClose) {
                this.destroy();
            }
        });
        this.on('newListener', (type) => {
            if (type == 'data') {
                this.flowing = true;
                this.read();
            }
            if (type == 'readable') {
                this.read(0);
            }
        });
        this.open();
    }

    open() {
        fs.open(this.path, this.flags, this.mode, (err, fd) => {
            if (err) {
                if (this.autoClose) {
                    this.destroy();
                    return this.emit('error', err);
                }
            }
            this.fd = fd;
            this.emit('open');
        });
    }

    read(n) {
        if (typeof this.fd != 'number') {
            return this.once('open', () => this.read());
        }
        n = parseInt(n, 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值