可写流

  • 可写流没有会创建,有内容的话会清空
  • 默认情况下一次能写 16 * 1024
  • 缓存区,第一次写入是真的向文件里写入,第二次在写入的时候放入到了缓存区里
  • 写入时候返回一个boolean类型,返回为false时不要在写入
  • 当内存和正在写入的内容消耗完后会触发 drain 事件。

读取的时候,读到几个就发射出几个,而可写流默认情况下一次能写入 16 * 1024。举例:读取 30 个,但是写一次写入3个,多出来的 27个则放入到缓存区里。第一次是真往文件里写入,后面则全部放到缓存区中,写完后则清空缓存区。

let fs = require('fs');let ws = fs.createWriteStream('./2.text', {flags: 'w', // 模式encoding: 'utf8', // 编码start: 0, // 从哪儿开始写highWaterMark: 3 // 最高的水位线 16 * 1024
});// 写入 写入的内容必须是字符串或者buffer
// flag 表示是否还有缓存空间 当写入的次数大于等于 highWaterMark 则返回flase
// 可读流配合可写流使用。flag 为了保证读取和写入可以节约内存,而不是疯狂的读
let flag = ws.write('1', 'utf8', () => {
});复制代码

举例子:一次能写3个,9个数字,先写入 9 8 7 然后等一等,等写完后在继续写入 6 5 4 。。。。比如一个人能吃馒头一次最多吃3个,如果不停的给他馒头,嘴里没有吃完则将馒头放到地上(缓存区里去)。等吃完在去拿来吃。

let fs = require('fs');let ws = fs.createWriteStream('./3.text', {flags: 'w', // 模式encoding: 'utf8', // 编码start: 0, // 从哪儿开始写highWaterMark: 3 // 最高的水位线 16 * 1024
});let i = 9;
function write() {// 是否可写入let flag = true;while(flag && i >= 0) {// 第一次 9 8 7 当是 7 的时候 flag 为falseflag = ws.write(i-- + '');}
}
// 只有嘴里塞满了吃完了。才会触发
// drain 触发时机,只有当嘴(highWaterMark)满了,才可能触发 drain
// 只有当嘴里的和地上的(内存里)都吃完了,才会触发 drain 方法
ws.on('drain', () => {console.log('吃完了。');// 继续吃write();
})
write();
复制代码

可写流原理

  • 默认属性
constructor(path, options = {}) {super();this.path = path;this.flags = options.flags || 'w';this.encoding = optins.encoding || 'utf8';this.start = options.start || 0;this.mode = options.mode || 0o666;this.autoClose = options.autoClose || true;this.highWaterMark = options.highWaterMark || 16 * 1024;// 先打开文件 异步调用 触发 open 事件后拿到 fdthis.open();
}
复制代码
  • open 打开文件
open() {fs.open(this.path, this.flags, this.mode, (err, fd) => {if (err) {// 是否需要关闭if(this.autoClose) {// 如果自动关闭就销毁文件描述符 fdthis.destory();}this.emit('error', err);return;}this.fd = fd;this.emit('open', this.fd);});
}
复制代码
  • 关闭文件
// 关闭文件
destory() {if(typeof this.fd !== 'number') {// 没有打开过this.emit('close');} else {fs.close(this.fd, () => {this.emit('close');});}
}
复制代码
  • write 写入
// 客户调用的是write方法写入内容
write(chunk, encoding = this.encoding, callback) {// 将 chunk 变成bufferchunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);this.len += chunk.length; // 维护缓存的长度let ret = this.len < this.highWaterMark;if (!ret) {this.needDrain = true; // 需要触发drain事件}if (this.writing) {// 正在写入应该放到内存中this.cach.push({chunk,encoding,callback});} else {// 第一次写入this._write(chunk, encoding, ()=>this.clearBuffer());}return ret; // 表示能不能继续写。false 表示下次写的时候就要占用更多内存
}
复制代码
  • _write && clearBuffer 方法实现
_write(chunk, encoding, buffer) {// write 同步调用 fd 还没获取到,获取到fd后在执行writeif (typeof this.fd !== 'number') {return this.once('open', this._write(chunk, encoding, buffer));}fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, byteWritten) => {this.pos += byteWritten;this.len -= byteWritten; // 每次写入后就要在内存中减少 相当于把馒头吃掉了this.clearBuffer(); // 第一次写完});
}clearBuffer() {let buffer = this.cache.shift();if (buffer) {// 缓存里有this._write(buffer.chunk, buffer.encoding, () => this.clearBuffer());} else {// 缓存里没有// 需要出发drain事件if (this.needDrain) {this.writing = false; // 不需要再写入到内存里。this.needDrain = false;this.emit('drain');}}
}
复制代码

完整代码

<!--WriteStream.js-->
let fs = require('fs');
let EventEmitter = require('events');class WriteStream extends EventEmitter {constructor(path, options = {}) {super();this.path = path;this.flags = options.flags || 'w';this.encoding = options.encoding || 'utf8';this.start = options.start || 0;this.pos = this.start;this.mode = options.mode || 0o666;this.autoClose = options.autoClose || true;this.highWaterMark = options.highWaterMark || 16 * 1024;// 先打开文件 异步调用 触发 open 事件后拿到 fdthis.open();// 写文件的时候需要的参数// 第一次写入是真的往文件里写入this.writing = false; // 默认第一次没有正在写入// 缓存区this.cache = [];// 维护一个变量 表示缓存的长度this.len = 0;// 是否触发 drain 事件this.needDrain = false;}// clearBuffer() {let buffer = this.cache.shift();if (buffer) {// 缓存里有this._write(buffer.chunk, buffer.encoding, () => this.clearBuffer());} else {// 缓存里没有// 需要出发drain事件if (this.needDrain) {this.writing = false; // 不需要再写入到内存里。this.needDrain = false;this.emit('drain');}}}_write(chunk, encoding, clearBuffer) {// write 同步调用 fd 还没获取到,获取到fd后在执行writeif (typeof this.fd !== 'number') {return this.once('open', ()=>this._write(chunk, encoding, clearBuffer));}fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, byteWritten) => {this.pos += byteWritten;this.len -= byteWritten; // 每次写入后就要在内存中减少 相当于把馒头吃掉了this.clearBuffer(); // 第一次写完});}// 客户调用的是write方法写入内容write(chunk, encoding = this.encoding, callback) {// 将 chunk 变成bufferchunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);this.len += chunk.length; // 维护缓存的长度let ret = this.len < this.highWaterMark;if (!ret) {this.needDrain = true; // 需要触发drain事件}if (this.writing) {// 正在写入应该放到内存中this.cache.push({chunk,encoding,callback});} else {this.writing = true;// 第一次写入this._write(chunk, encoding, ()=>this.clearBuffer());}return ret; // 表示能不能继续写。false 表示下次写的时候就要占用更多内存}// 关闭文件destory() {if(typeof this.fd !== 'number') {// 没有打开过this.emit('close');} else {fs.close(this.fd, () => {this.emit('close');});}}// 打开文件open() {fs.open(this.path, this.flags, this.mode, (err, fd) => {if (err) {// 是否需要关闭if(this.autoClose) {// 如果自动关闭就销毁文件描述符 fdthis.destory();}this.emit('error', err);return;}this.fd = fd;this.emit('open', this.fd);});}}module.exports = WriteStream;<!--应用-->
let fs = require('fs');
let WS = require('./WriteStream');
let ws = new WS('./3.text', {flags: 'w', // 文件如果不存在则创建,文件存在则清空highWaterMark: 3, // 设置当前缓存区大小即嘴有多大encoding: 'utf8', // 文件里存放的是二进制start: 0,autoClose: true, // 自动关闭mode: 0o666 // 可读可写
});let i = 9;function write() {let flag = true;while(flag && i >= 0) {flag = ws.write(i-- + '', 'utf8');}
}
// drain 触发时机,只有当嘴(highWaterMark)满了,才可能触发 drain
// 只有当嘴里的和地上的(内存里)都吃完了,才会触发 drain 方法
write();
ws.on('drain', () => {console.log('写完了');write();
});
复制代码

node 流学习笔记 - 可写流相关推荐

  1. node.js学习笔记之写文件

    node.js之写文件 //---------------optfile.js------------------ var  fs=  require('fs'); module.exports={ ...

  2. Redis实现分布式限流(学习笔记

    Redis实现分布式限流(学习笔记2022.07.09) 前言: 以下实现都是基于: spring-boot-starter-web + spring-boot-starter-data-redis ...

  3. 千锋Node.js学习笔记

    千锋Node.js学习笔记 文章目录 千锋Node.js学习笔记 写在前面 1. 认识Node.js 2. NVM 3. NPM 4. NRM 5. NPX 6. 模块/包与CommonJS 7. 常 ...

  4. 唤醒手腕 - 前端服务器端开发 Node.Js 学习笔记(学习中,更新中)

    唤醒手腕 - Node.Js 学习笔记 唤醒手腕个人的学习记录,时间在2021年12月13日 ~ 2021年12月14日,学习方式看官方文档和B站视频,如有错误或者代码问题的地方,欢迎C站大佬能够帮忙 ...

  5. node.js学习笔记5——核心模块1

    node.js学习笔记5--核心模块1 Node.js核心模块主要内容包括:(1)全局对象 (2)常用工具 (3)事件机制 (4)文件系统访问 (5)HTTP服务器与客户端 一: 全局对象 Node. ...

  6. node.js学习笔记

    # node.js学习笔记标签(空格分隔): node.js---## 一 内置模块学习 ### 1. http 模块 ``` //1 导入http模块 const http =require('ht ...

  7. Node.js学习笔记8

    Node.js学习笔记8 HTTP服务器与客户端 Node.js的http模块,封装了一个高效的HTTP服务器和一个简易的HTTP客户端 http.server是一个基于事件的HTTP服务器,核心由N ...

  8. node.js 学习笔记(二)模板引擎和C/S渲染

    node.js 学习笔记(二)模板引擎和C/S渲染 文章目录 node.js 学习笔记(二)模板引擎和C/S渲染 一.初步实现Apache功能 1.1 使用模板引擎 1.2 在 node 中使用模板引 ...

  9. node.js学习笔记14—微型社交网站

    node.js学习笔记14-微型社交网站 1.功能分析 微博是以用户为中心,因此需要有注册和登录功能. 微博最核心的功能是信息的发表,这个功能包括许多方面,包括:数据库访问,前端显示等. 一个完整的微 ...

最新文章

  1. jQuery Mobile设置边距的宽度和颜色
  2. 类的静态成员变量和静态成员函数的使用方法三
  3. 说说第三方支付接口开发及开发中遇到的坑爹问题
  4. python collections模块(数据结构常用模块)计数器Counter 双向队列deque 默认字典defaultdict 有序字典OrderedDict 可命名元组namedtuple
  5. 海量数据 - join处理
  6. redis小功能大用处-bitmaps
  7. BRAND-NEW OF DOUBLES工作组合的新作品
  8. cassandra使用心得_使用Cassandra和Nutch爬网
  9. [react] 请描述下事件在react中的处理方式是什么?
  10. OpenCL 第7课:旋转变换(1)
  11. # SDN第五次上机作业
  12. 一个demo学会c#
  13. js权威指南---学习笔记01
  14. 在AspNetCore中json序列化日期格式自定义
  15. 基于SSM框架的考勤签到请假系统
  16. 戴尔G5 5590重装系统
  17. 快乐暑假(八)——欧拉回路和哈密顿回路
  18. 3D环绕音乐单页网站源码
  19. 推荐搜索的冷启动问题
  20. 解决 Failed to fetch http://172.6.0.2/ubuntu/dists/jammy/main/binary-i386/Packages 404 Not Found问题

热门文章

  1. 数据库范式1NF 2NF 3NF BCNF
  2. 深度信念网络Deep Belief Networks资料汇总
  3. 随机重命名MP3文件
  4. C++实用技巧(四)
  5. 数学之美番外篇:快排为什么那样快
  6. 第三日:继续恢复网站
  7. Linux内核分析---进程的创建,执行与切换
  8. MyBatis拦截器原理探究MyBatis拦截器原理探究
  9. Saltstack基本安装部署
  10. React系列---React+Redux工程目录结构划分