问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何从字符串中的Node.js创建流

发布网友 发布时间:2022-04-25 13:01

我来回答

2个回答

懂视网 时间:2022-05-15 03:30

这次给大家带来怎样利用node做出可读流动模式,利用node做出可读流动模式的注意事项有哪些,下面就是实战案例,一起来看一下。

node的可读流基于事件

可读流之流动模式,这种流动模式会有一个"开关",每次当"开关"开启的时候,流动模式起作用,如果将这个"开关"设置成暂停的话,那么,这个可读流将不会去读取文件,直到将这个"开关"重新置为流动。

读取文件流程

读取文件内容的流程,主要为:

  1. 打开文件,打开文件成功,将触发open事件,如果打开失败,触发error事件和close事件,将文件关闭。

  2. 开始读取文件中的内容,监听data事件,数据处于流动状态,可通过修改开关的状态来暂停读取。

  3. 每次读取到的内容放入缓存中,并通过data事件将数据发布出去。

  4. 当文件中的内容读取完毕之后,将文件关闭。

这一系列动作都是基于事件来进行操作的,而node中的事件我们都知道是一种发布订阅模式来实现的。

下面我们来看一看,node是如何使用可读流来读取文件中的内容?

node 可读流参数

首先我们通过fs模块来创建一个可读流,可读流接受两个参数:

  1. 第一个参数是要读取的文件地址,在这里指明你要读取哪个文件。

  2. 第二个参数是可选项,这个参数是一个对象,用来指定可读流的一些具体的参数。

如下几个参数我们来一一说明:

  • highWaterMark:设置高水位线,这个参数主要用于在读取文件时,可读流会将文件中的内容读取到缓存当中,而这里我们需要创建一个buffer来缓存这些数据,所以这个参数是用来设置buffer的大小,如果不对这个参数进行设置的话,可读流默认的配置64k。

  • flags:这个参数主要用于设置文件的执行模式,比如说我们具体的操作适用于读取文件还是写入文件等这些操作。如果是写入文件的话那我们,使用的是w。如果是读取文件的话那这个操作符就应该是r。

  • 下面这张表格就说明了不同的符号代表不同含义:

    符号含义
    r读文件,文件不存在报错
    r+读取并写入,文件不存在报错
    rs同步读取文件并忽略缓存
    w写入文件,不存在则创建,存在则清空
    wx排它写入文件
    w+读取并写入文件,不存在则创建,存在则清空
    wx+和w+类似,排他方式打开
    a追加写入
    ax与a类似,排他方式写入
    a+读取并追加写入,不存在则创建
    ax+作用与a+类似,但是以排他方式打开文件
  • autoClose:这个参数主要用于,对文件的关闭的一些控制。如果文件再打开的过程或者其他操作的过程中出现了错误的情况下,需要将文件进行关闭。那这个参数是设置文件是否自动关闭的功能。

  • encoding:node中用buffer来读取文件操作的东西二进制数据。这些数据展现出来的话我们是一堆乱码,所以需要,要我们对这个数据指定一个具体的编码格式。然后将会对这些数据进行编码转化,这样转化出来的数据就是我们能看懂的数据。

  • starts:这个参数主要用于指定从什么位置开始读取文件中的内容,默认的话是从零开始。

  • ends:这个参数主要用于指定定具体要读取文件多长的数据,这里需要说明一下,这个参数是包括本身的位置,也就是所谓的包前和包后。

  • 下面我们来看看可读流具体例子:

    let fs = require("fs");
    let rs = fs.createReadStream("./a.js", {
     highWaterMark: 3,
     encoding: "utf8",
     autoClose: true,
     start: 0,
     end: 9
    });
    rs.on("open", () => {console.log("open");});
    rs.on("close", () => {console.log("close");});
    rs.on("data", data => {
     console.log(data);
     rs.pause();//暂停读取 此时流动模式为暂停模式
    });
    setInterval(() => {
     rs.resume();//重新设置为流动模式,开始读取数据
    }, 1000);
    rs.on("end", () => { console.log("end"); });
    rs.on("error", err => { console.log(err); });

    手写可读流第一步

    上面我们说过,node可读流是基于node的核心模块事件来完成的,所以在实现我们自己的可读流时需要继承events模块,代码如下:

    let fs = require('fs');
    let EventEmitter = require('events');
    class ReadStream extends EventEmitter {
    }

    继承了EventEmitter类,我们就可以使用EventEmitter类中的各个方法,并且同样是采用发布订阅的模式了处理事件。

    第二步:处理可读流配置的参数

    上面我们提到,node中创建可读流时可以对这个流配置具体的参数,比如

    let rs = fs.createReadStream("./a.js", {
     highWaterMark: 3,
     encoding: "utf8",
     autoClose: true,
     start: 0,
     end: 9
    });

    那么对于这些参数,我们自己实现的可读流类也需要对这些参数进行处理,那么这些参数该如何进行处理呢?

    constructor(path, options = {}) {
     super();
     this.path = path; //指定要读取的文件地址
     this.highWaterMark = options.highWaterMark || 64 * 1024;
     this.autoClose = options.autoClose || true; //是否自动关闭文件
     this.start = options.start || 0; //从文件哪个位置开始读取
     this.pos = this.start; // pos会随着读取的位置改变
     this.end = options.end || null; // null表示没传递
     this.encoding = options.encoding || null;// buffer编码
     this.flags = options.flags || 'r';
     this.flowing = null; // 模式开关
     this.buffer = Buffer.alloc(this.highWaterMark);// 根据设置创建一个buffer存储读出来的数
     this.open();
    }

    通常配置的原则是以用户配置的参数为准,如果用户没有对这个参数进行设置的话,就采用默认的配置。

    实现可读流第三步:打开文件

    这里原理是使用node模块fs中的open方法。首先我们来回顾下fs.open()方法的使用。

    fs.open(filename,flags,[mode],callback);
    //实例
    fs.open('./1,txt','r',function(err,fd){});

    这里需要说明下,回调函数callback中有2个参数:

    1. 第一个是error,node中异步回调都会返回的一个参数,用来说明具体的错误信息

    2. 第二个参数是fd,是文件描述符,用来标识文件,等价于open函数的第一个参数

    好了,现在我们来看看我们自己的可读流的open方法该如何实现吧:

    open() {
     fs.open(this.path, this.flags, (err, fd) => { 
     //fd标识的就是当前this.path这个文件,从3开始(number类型)
     if (err) {
     if (this.autoClose) { // 如果需要自动关闭则去关闭文件
     this.destroy(); // 销毁(关闭文件,触发关闭事件)
     }
     this.emit('error', err); // 如果有错误触发error事件
     return;
     }
     this.fd = fd; // 保存文件描述符
     this.emit('open', this.fd); // 触发文件的打开的方法
     });
    }

    从代码上我们可以看出:

    fs.open函数是异步函数,也就是说callback是异步执行的,在成功打开文件的情况下,fd这个属性也是异步获取到的,这点需要注意。

    另外重要的一点是,如果在打开文件发生错误时,则表明打开文件失败,那么此时就需要将文件关闭。

    实现可读流第四步:读取文件内容

    上面我们详细说过,可读流自身定义了一个"开关",当我们要读取文件中的内容的时候,我们需要将这个"开关"打开,那么node可读流本身是如何来打开这个"开关"的呢?

    监听data事件

    node可读流通过监听data事件来实现这个"开关"的开启:

    rs.on("data", data => {
     console.log(data);
    });

    当用户监听data事件的时候,"开关"开启,不停的从文件中读取内容。那么node是怎么监听data事件的呢?
    答案就是 事件模块的newListener

    这是因为node可读流是基于事件的,而事件中,服务器就可以通过newListener事件监听到从用户这边过来的所有事件,每个事件都有对应的类型,当用户监听的是data事件的时候,我们就可以获取到,然后就可以去读取文件中的内容了,那我们自己的可读流该如何实现呢?

    // 监听newListener事件,看是否监听了data事件,如果监听了data事件的话,就开始启动流动模式,读取文件中的内容
    this.on("newListener", type => {
     if (type === "data") {
     // 开启流动模式,开始读取文件中的内容
     this.flowing = true;
     this.read();
     }
    });

    好了,知道了这个"开关"是如何打开的,那么这个时候就到了真正读取文件中内容的关键时候了,先上代码先:

    read() {
     // 第一次读取文件的话,有可能文件是还没有打开的,此时this.fd可能还没有值
     if (typeof this.fd !== "number") {
     // 如果此时文件还是没有打开的话,就触发一次open事件,这样文件就真的打开了,然后再读取
     return this.once("open", () => this.read());
     }
     // 具体每次读取多少个字符,需要进行计算,因为最后一次读取倒的可能比highWaterMark小
     let howMuchRead = this.end ? Math.min(this.end - this.pos + 1, this.highWaterMark) : this.highWaterMark;
     fs.read(this.fd, this.buffer, 0, howMuchRead, this.pos, (err, byteRead) => {
     // this.pos 是每次读取文件读取的位置,是一个偏移量,每次读取会发生变化
     this.pos += byteRead;
     // 将读取到的内容转换成字符串串,然后通过data事件,将内容发布出去
     let srr = this.encoding ? this.buffer.slice(0, byteRead).toString(this.encoding) : this.buffer.slice(0, byteRead);
     // 将内容通过data事件发布出去
     this.emit("data", srr);
     // 当读取到到内容长度和设置的highWaterMark一致的话,并且还是流动模式的话,就继续读取
     if ((byteRead === this.highWaterMark) && this.flowing) {
     return this.read();
     }
     // 没有更多的内容了,此时表示文件中的内容已经读取完毕
     if (byteRead < this.highWaterMark) {
     // 读取完成,发布end方法,并关闭文件
     this.emit("end");
     this.destory();
     }
     });
    }

    这里我们特别要注意的是:

    1. 文件是否已经打开,是否获取到fd,如果没有打开的话,则再次触发open方法

    2. 分批次读取文件内容,每次读取的内容是变化的,所以位置和偏移量是要动态计算的

    3. 控制读取停止的条件。

    实现可读流第五步:关闭文件

    好了,到现在,基础的读取工作已经完成,那么就需要将文件关闭了,上面的open和read方法里面都调用了一个方法:destory,没错,这个就是关闭文件的方法,好了,那么我们来看看这个方法该如何实现吧

    destory() {
     if (typeof this.fd !== "number") {
     // 发布close事件
     return this.emit("close");
     }
     // 将文件关闭,发布close事件
     fs.close(this.fd, () => {
     this.emit("close");
     });
    }

    当然这块的原理就是调用fs模块的close方法啦。

    实现可读流第六步:暂停和恢复

    既然都说了,node可读流有一个神奇的"开关",就像大坝的阀门一样,可以控制水的流动,同样也可以控制水的暂停啦。当然在node可读流中的暂停是停止对文件的读取,恢复就是将开关打开,继续读取文件内容,那么这两个分别对应的方法就是pause()和resume()方法。

    那么我们自己的可读流类里面该如何实现这两个方法的功能呢?非常简单:

    我们在定义类的私有属性的时候,定义了这样一个属性flowing,当它的值为true时表示开关打开,反之关闭。

    pause() {
     this.flowing = false;// 将流动模式设置成暂停模式,不会读取文件
    }
    resume() {
     this.flowing = true;//将模式设置成流动模式,可以读取文件
     this.read();// 重新开始读取文件
    }

    相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

    推荐阅读:

    怎样利用JS做出引用传递与值传递

    使用JS实做出加密解密操作

    热心网友 时间:2022-05-15 00:38

    1. 刚创建的一个新实例stream模块,并根据您的需求定制它:
    var Stream = require('stream')
    var stream = new Stream()
    stream.pipe = function(dest) {
    dest.write('your string')
    }
    stream.pipe(process.stdout) // in this case the terminal, change to ya-csv

    var Stream = require('stream')
    var stream = new Stream()
    stream.on('data', function(data) {
    process.stdout.write(data) // change process.stdout to ya-csv
    })
    stream.emit('data', 'this is my string')

    2. 将字符串转换为流,通过一个流:
    through().pause().queue('your string').end()
    例如:
    var through = require('through')
    // Create a paused stream and buffer some data into it:
    var stream = through().pause().queue('your string').end()
    // Pass stream around:
    callback(null, stream)
    // Now that a consumer has attached, don't forget to resume the stream:
    stream.resume()
    更新:@substack刚刚发布的模块。这就像一个通过()流,但自上nextTick流,当且仅当别人有它在
    var resumer = require('resumer')
    var stream = resumer().queue('your string').end()
    // Now pass `stream` around. No need to resume, it happens automatically.
    该resumer().queue('some string').end()idioms是我现在创建adhoc流。这是方便多了,你可以return流,他们还是会得到
    3. JavaScript是鸭子类型的 CodeGo.net,所以如果你只是复制一个可读的流的API,它会工作得很好。事实上,你也许可以不是大多数的或刚刚离开他们作为存根,所有你需要的是什么样的你节点的预建EventEmitter类来处理事件,也让你不必addListener和这样的自己。 这里是你如何可能它的CoffeeScript:
    class StringStream extends require('events').EventEmitter
    constructor: (@string) -> super()
    readable: true
    writable: false
    setEncoding: -> throw 'not implemented'
    pause: -> # nothing to do
    resume: -> # nothing to do
    destroy: -> # nothing to do
    pipe: -> throw 'not implemented'
    send: ->
    @emit 'data', @string
    @emit 'end'
    那么你也喜欢这样:
    stream = new StringStream someString
    doSomethingWith stream
    stream.send()
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    苹果电脑电池充不进电苹果电脑充不进去电是怎么回事 苹果电脑不充电没反应苹果电脑充电指示灯不亮充不了电怎么办 狗狗更加忠诚护家、善解人意,养一只宠物陪伴自己,泰迪能长多大... 描写泰迪狗的外形和特点的句子 国外留学有用吗 花钱出国留学有用吗 !这叫什么号 百万医疗赔付后是否可以续保 前一年理赔过医疗险还能续保吗? 医疗住院险理赔后还能购买吗? 女朋友最近有事忙不来,叫我弄一篇毕业论文,我没有优盘,昨天去网吧写... word高手 !!来啊!怎样让 word的窗口总是在桌面的最前? 带有“静”字的成语有哪些? node.js怎么往文本文档中写入数据 请问研究生毕业论文各高校会保存几年? Node.js-nodejs可否读取远程文件到本地 有没有像php file 有关静字的成语大全 毕业论文,是学校保留的吗?一般要保留多久? 带有静字的词语 怎样把word文档置于最前(在打开网页的情况下) node.js pipe是异步吗 关于静字的组词 本科毕业论文电子版学校保留几年,有相应条例么? 自考本科纸质的毕业论文在学校里存放几年? 带有“静”字的词语有哪些? 本科毕业论文保存几年 静字的由来和最初的含义 在word2010中做题:在文本最前面插入类别为“文本信息”,名称为“新闻提... 毕业论文写完了存放在哪里? 怎么让word始终显示在最上层? 写出四个带有“静”字的词语 毕业论文只能在一个电脑上写保存吗 推荐四个带有“静”字的词语。 使用node.js,实现一个简单的接口服务器的功能 毕业之后自己还需要存着毕业论文的相关资料吗? 带静字的词语 河南师范大学的毕业论文真的保存到光盘里了吗?保存多久呀? 深入浅出Node.js的内容简介 关于静的四字词语。 带有“静”字的四字词和成语 我的QQ号登陆不上去怎么回事啊? 在Node.js中,怎么向spawn和exec产生的子进程的stdin输入东西 本科优秀毕业论文一般学校会保存其电子版吗 带有“静”字诗句有哪些? 简述node.js的流的作用? nodejs 静态服务器和http服务器有什么区别 我的QQ账号不能解封? 我的QQ号登录不上了咋办 我QQ号怎么上不去啊 我QQ号上不去了