Pro

一个createWriteStream的简单实现,以求能增加对可写流的理解与应用。

参数配置

/*** createWriteStream* @param1 path* @param2 options*/
let fs = require('fs');
let ws = fs.createWriteStream('./1.txt',{flags:'w'//文件的打开模式,mode:0o666//文件的权限设置,encoding:'utf8'//写入文件的字符的编码,highWaterMark:3//最高水位线,start:0 //写入文件的起始索引位置     ,autoClose:true//是否自动关闭文档
})
复制代码

createWriteStream类的实例化

  • 实例化一个createWriteStream

    • pathoptions挂载在createWriteStream的实例上,除此之外再在实例上挂载以下属性

      • self.fd=null:文件打开后返回的文件描述符
      • self.pos=self.start:用于表示文件真正写入时的指针位置
      • self.Buffer=[]:用来表示文件的缓冲区
      • self.len=null:用来表示缓冲区此时的大小
      • self.isWriting=false:用来表示是否正在真正写入文件
    • 调用open方法,打开文件(发射open事件)

实例write方法的执行流程

  • wirte方法接收三个参数,chunk要写入的内容,encoding要进行的,cb回调函数。
  • write执行流程:
    • 判断传入的chunk是否为buffer,如果不是,则转换成buffer,用于转化编码依据传入的encoding参数。
    • 更新Buffer缓冲区的len长度,让len加上该次chunk的长度
    • 判断len是否已经超过highWaterMark,将值存入flag
    • 判断是否处于isWriting状态:
      • 是,则先加chunk写入实例对象下的Buffer缓冲区
      • 否,更新isWriting,接将参数传递给实例下的_write方法写入文件
    • 返回flag

实例_write方法的执行流程

此方法用于真正写入文件

  • 查看实例的fd属性是否存在(文件是否打开成功)

    • 成功,调用fs模块的write方法正式写入数据

      • 更新实例对象下的len以及pos属性
      • 调用clearBuffer方法将缓冲区的内容写入
      • 调用write方法传入的回调函数cb
    • 失败,订阅一个open事件(open事件将会在open方法中被发射),在订阅中的回调方法中再次以相同的参数调用_write方法

实例clearBuffer方法

  • 从缓冲区中取出一个数据

    • 如果数据存在,调用_write方法
    • 如果数据不存在,将isWriting更改为false,发射drain事件

实现源码以及测试文件

let fs = require('fs');
let EventEmitter = require('events');class WriteStream extends EventEmitter {constructor(path, options) {super();let self = this;Object.assign(self, options); //还需设置默认值self.path = path;self.isWriting = false;self.Buffer = []; //源码中为链表实现的缓冲区self.len = null;self.pos = self.start; //初始化写入位置self.fd = null;self.open();}open() {let self = this;fs.open(self.path, self.flags, self.mode, (err, fd) => {self.fd = fd;if (err) return self.destroy(err);self.emit('open');});}destroy(err) {fs.close(this.fd, () => {this.emit('error', err);});}write(chunk, encoding, cb) {let self = this, ret = null;encoding = encoding?encoding:self.encoding; //优先使用write传入的编码方式chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);self.len += chunk.length;ret = self.highWaterMark > self.len; //判断当前最新的缓冲区是否已达到最高水位线if (self.isWriting) { //说明正在调用底层方法真正写入文件,先写入Bufferself.Buffer.push({chunk, cb});} else {self.isWriting = true;self._write(chunk, cb, () => self.clearBuffer());}return ret;}_write(chunk, cb, clear) {let self = this;if (!self.fd) return self.once('open', () => {self._write(chunk, cb, clear)});fs.write(self.fd, chunk, 0, chunk.length, self.pos, (err, bytesWritten) => {if (err) {if (self.autoClose) {self.destroy();self.emit('error', err);}}self.len -= bytesWritten;self.pos += bytesWritten;cb && cb();clear && clear();});}clearBuffer() {let self = this, data = null;data = self.Buffer.shift();if (data) {self._write(data.chunk, data.cb, () => self.clearBuffer());} else { //此时说明缓冲区已无数据self.isWriting = false;self.emit('drain');}}
}module.exports = WriteStream;
复制代码

测试文件:

let WriteStream = require('./practice');
let ws = new WriteStream('./1.txt',{flags:'w',mode:0o666,start:0,encoding:'utf8',autoClose:true //当流写完之后自动关闭文件,highWaterMark:3
});
let n = 9;
ws.on('error',(err)=>{console.log(err)
})
function write(){let flag = true;while(flag&&n>0){flag = ws.write(n+"",'utf8',()=>{console.log('ok');});n--;console.log('flag=',flag)}ws.once('drain',()=>{console.log('drain');write();})
}
// ws.on('drain',()=>{
//   console.log('drain');
//   write();
// })
write();复制代码

参考资料: nodejs.org/dist/latest…

简明writeStream实现相关推荐

  1. Visual SourceSafe简明培训教程

      名称 Visual SourceSafe简明培训教程 (Visual SourceSafe Training Short Course) 作者 晨光(Morning) 简介 对于采用Visual ...

  2. python实用程序育儿法_Python多线程 简明例子

    Python多线程 简明例子 (2010-03-11 15:15:09) Python多线程 简明例子 综述 多线程是程序设计中的一个重要方面,尤其是在服务器Deamon程序方面.无论何种系统,线程调 ...

  3. CGIC简明教程(转摘)

    CGIC简明教程 本系列的目的是演示如何使用C语言的CGI库"CGIC"完成Web开发的各种要求. *********************************     基础 ...

  4. 简明docker教程

    简明docker教程 一.什么是docker 二.docker与虚拟机比较 三.安装docker 四.基本概念 1.镜像 2.容器 3.数据卷 4.挂载 五.参考资料 有收获的话请加颗小星星,没有收获 ...

  5. markdown简明语法

    # markdown简明语法 标签(空格分隔): markdown 本语法只涵盖了常用的内容 [toc] 标题 标题 标题 语法为:根据需求 可以指定 不同大小的标题# 顶级## 次级### 次次级. ...

  6. Python 正在从简明转向臃肿,从实用转向媚俗

    国庆长假期间,Python3.9正式推出,各大IT平台和众多自媒体纷纷火力全开,热推Python3.9的新增特性.然而,除了媒体的自娱自乐,几乎所有的程序员都对此表示无感.我甚至觉得 ,每一次的版本升 ...

  7. 畅销书《简明的TensorFlow2》作者李卓桓开讲啦!

    为了帮助机器学习爱好者们更快地上手TensorFlow 2,同时掌握多端部署能力,AI研习社联手图灵社区,有幸邀请到最近热门的畅销书<简明的TensorFlow2>三位作者:谷歌开发者专家 ...

  8. 今晚直播 | 来自《简明的 TensorFlow 2》作者,Google开发者专家的分享

    作为一个端到端开源机器学习平台,TensorFlow的出现极大地激发了机器学习爱好者们的创作热情,它不仅拥有一个全面而灵活的生态系统,其中包含各种工具.库和社区资源,可助力研究人员推动先进机器学习技术 ...

  9. SAP项目各模块简明调研提纲(一本通)

    SAP项目各模块简明调研提纲(一本通) SD-销售/市场管理 1. 围绕总体销售组织.产品和销售业务类型等总体沟通业务和需求目标 2. 结合现有系统的销售计划.销售订单.价格管控.销售物流发货与仓储财 ...

最新文章

  1. HDU 2602 Bone Collector DP(01背包)
  2. 给Linux系统/网络管理员的nmap的29个实用例子
  3. java的final修饰_java final 修饰符详解
  4. php关闭按钮,C#_winform去掉右上角关闭按钮的方法,一种方法是可以在窗体的属性 - phpStudy...
  5. java继承与多态 心得体会_继承与多态感想
  6. bzoj2648/2716 kdtree
  7. 浅谈Netty相关概念
  8. DenseNet解析
  9. VMware vSphere重置系统配置
  10. html table相同值合并单元格,ElementUI表格列相同值自动合并单元格( 单列 )
  11. APICloud进行窗口和页面操作
  12. Windows10专业版系统镜镜像
  13. MySQL数据库——常用数据库大汇总(附带优缺点)
  14. 动态规划-骨头收集者(一维数组,二维数组)
  15. C# 设置Excel打印选项及打印excel文档
  16. 一沙一世界,一叶一菩提
  17. python怎么升级python的pip
  18. chgrp命令用法举例
  19. android studio下使用TUTK SDK
  20. 二进制,十进制,八进制,十六进制之间的进制转换

热门文章

  1. java状态模式所有情况_轻松掌握Java状态模式
  2. java wcf 未提供用户名_WCF的用户名密码认证
  3. python 发送邮件正文字体设置_python 文字 坐标python smtplib模块发送SSL/TLS安全邮件实例...
  4. python函数装饰器参数 参数_【转】python 装饰器功能以及函数参数使用
  5. 查看linux内存存储空间不足,Linux 下判断Server 内存是否不足
  6. svm常用核函数及选择核函数的方法
  7. anki怎么设置学习计划_新媒体企业品牌营销策划公众号运营规划线上推广内容管理sop工作流程计划方案表格模板新手小白零基础怎么学习写作软文涨粉技巧攻略下载...
  8. python csv文件写入失败_python解析csv文件失败
  9. linux操作系统中查找某个进程,在linux下查看有哪些操作系统进程正在使用某一个共享内存段...
  10. Invalid bound statement (not found) 解决方案