浅谈Nodejs中的可写流write与实现方法
本篇文章带大家了解一下Nodejs中的可写流write,介绍一下Node可写流write的实现。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。
【推荐学习:《nodejs 教程》】
可写流-Writable
fs.createWriteStream调用例子
- 首次读取的数据会真实写入目标文件
- 其余次读取的数据要根据读取数据是否超出highWaterMark ,是的话存入缓存区等待写入目标文件中
const fs = require("fs"); const path = require("path"); const bPath = path.join(__dirname, "b.txt"); let ws = fs.createWriteStream(bPath, { flags: "w", encoding: "utf-8", autoClose: true, start: 0, highWaterMark: 3, }); ws.on("open", function (fd) { console.log("open", fd); }); ws.on("close", function () { console.log("close"); }); //string 或者buffer,ws.write 还有一个boolea的返回值 ws.write("1"); //flag 表示 当前要写的值是直接是否直接写入文件,不能超出了单次最大写入值highWaterMark let flag = ws.write("1"); console.log({ flag });//true flag = ws.write("1"); console.log({ flag });//false flag = ws.write("1"); console.log({ flag });//false flag = ws.write("14444444"); console.log({ flag });//false ws.end(); //write+close,没有调用 end 是不会调用 触发close的,看到这里的小伙伴可以尝试注释end() 看看close的console是否有打印
- 效果
自定义可写流initWriteStream
继承EventEmitter发布订阅
const EventEmitter = require("events"); const fs = require("fs"); class WriteStream extends EventEmitter {} module.exports = WriteStream;
链表生成队列做文件读取的缓存
链表&队列的实现
https://juejin.cn/post/6973847774752145445
// 用链表 生成队列 对 文件缓存区的读取 进行优化 const Queue = require("./queue");
初始化实例默认数据constructor()
constructor(path, options = {}) { super(); this.path = path; this.flags = options.flags || "w"; this.encoding = options.encoding || "utf8"; this.mode = options.mode || 0o666; //默认8进制 ,6 6 6 三组分别的权限是 可读可写 this.autoClose = options.start || 0; this.highWaterMark = options.highWaterMark || 16 * 1024; //默认一次读取16个字节的数据 this.len = 0; //用于维持有多少数据还没有被写入文件中 //是否根据等待当前读取的最大文数据 排空后再写入 this.needDrain = false; // // 缓存队列 用于存放 非第一次的文件读取 到的数据,因为第一次读取 直接塞入目标文件中 // 除第一次 的文件读取数据的都存放再缓存中 // this.cache = []; // 队列做缓存 this.cache = new Queue(); // 标记是否是第一次写入目标文件的标识 this.writing = false; this.start = options.start || 0; this.offset = this.start; //偏移量 this.open(); }
this.mode 文件操作权限 默认0o666(0o表示8进制)
3个6所占位置分别对应:文件所属用户对它的权限 ;文件所属用户组用户对它的权限;表示其他用户对它的权限
权限由:r--可读(对应数值4),w--可写(对应数值2),x--可执行(对应数值1,例如文件夹下有 .exe 这样的标识 说明点击可以直接执行)组成
所以默认情况下3组用户对文件的操作权限都是可读可写
open()
- 调用fs.open()
- 回调emit实例open方法,fs.open的返回值fd做参数传入
open() { fs.open(this.path, this.flags, this.mode, (err, fd) => { this.fd = fd; this.emit("open", fd); }); }
write()
- 转化实例传入的需要写入的文件数据格式为buffer
- 判断写入数据长度是否大于highWaterMark,如果达到预期后,文件读取到的数据存放再缓存里 不直接写入目标文件(这里要排除是否是第一次读取文件)
- 执行实例write 传入的cb 并调用clearBuffer 清空缓存
- 判断 是否是第一次读取,第一次读取 直接写入调用 _write(待实现)
- 缓存队列尾部offer 当前读取到的数据等待写入目标文件
write(chunk, encoding = this.encoding, cb = () => {}) { // 将数据全部转换成buffer chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); this.len += chunk.length; // console.log({chunk},this.len ) let returnValue = this.len < this.highWaterMark; //当数据写入后,需要在手动的将this.len-- this.needDrain = !returnValue; //如果达到预期 后 的文件读取 到数据存放再缓存里 不直接写入目标文件 //清空缓存 对用户传入的回调 进行二次包装 let userCb = cb; cb = () => { userCb(); //清空buffer this.clearBuffer();//马上实现 }; //此时需要判断 是否是第一次读取,第一次读取 直接写入调用 _write if (!this.writing) { // 第一次||缓存队列已清空完毕 this.writing = true; // console.log("first write"); this._write(chunk, encoding, cb);//马上实现 } else { //缓存队列尾部offer 当前读取到的数据等待写入目标文件 this.cache.offer({ chunk, encoding, cb, }); } return returnValue; }
clearBuffer()依次清空缓存队列
- 队列执行顺序,先进先出原则
- this.cache.poll() 依次拿取头部数据执行this._write写入目标文件
- 缓存队列poll出来的data如果不存在,则说明是第一次写入的行为||缓存队列已清空。this.writing = false; 下次的文件读取可以直接写入目标文件
- 如果this.needDrain又达到预期,文件读取到数据存放再缓存里 不直接写入目标文件
clearBuffer() { //写入成功后 调用 clearBuffer--》写入缓存第一个,第一个完成后,再继续 第二个 let data = this.cache.poll(); // console.log('this.cache',this.cache) if (data) { //有值 写入文件 this._write(data.chunk, data.encoding, data.cb); } else { this.writing = false; if (this.needDrain) { // 如果是缓存,触发drain this.emit("drain"); } } }
_write()
- fs.open()是异步的,成功读取后fd会是一个number类型
- 根据fd的type 决定是否订阅一次open,并回调自己(直到fd类型为number)
- fd类型为number:调用fs.write,写入当前的chunk,
_write(chunk, encoding, cb) { if (typeof this.fd !== "number") { return this.once("open", () => this._write(chunk, encoding, cb)); } fs.write(this.fd, chunk, 0, chunk.length, this.offset, (err, written) => { this.offset += written; //维护偏移量 this.len -= written; //把缓存的个数减少 cb(); //写入成功 // console.log(this.cache); }); }
测试自定义的Writable
const WriteStream = require("./initWriteStream"); let ws = new WriteStream(bPath, { highWaterMark: 3, }); let i = 0; function write() { //写入0-9个 let flag = true; while (i < 10 && flag) { flag = ws.write(i++ + ""); console.log(flag); } } ws.on("drain", function () { // 只有当我们写入的数据达到预期,并且数据被清空后才会触发drain ⌚️ console.log("写完了"); write(); }); write();
- 10个数字,依次写入,3次达到最大预期值,然后依次清空了3次缓存结果符合预期
- 目标文件中查看是否正确写入了我们预期的数值
更多编程相关知识,请访问:编程视频!!
以上是浅谈Nodejs中的可写流write与实现方法的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

Node.js 是一种服务器端 JavaScript 运行时,而 Vue.js 是一个客户端 JavaScript 框架,用于创建交互式用户界面。Node.js 用于服务器端开发,如后端服务 API 开发和数据处理,而 Vue.js 用于客户端开发,如单页面应用程序和响应式用户界面。

要连接 MySQL 数据库,需要遵循以下步骤:安装 mysql2 驱动程序。使用 mysql2.createConnection() 创建连接对象,其中包含主机地址、端口、用户名、密码和数据库名称。使用 connection.query() 执行查询。最后使用 connection.end() 结束连接。

Node.js 安装目录中有两个与 npm 相关的文件:npm 和 npm.cmd,区别如下:扩展名不同:npm 是可执行文件,npm.cmd 是命令窗口快捷方式。Windows 用户:npm.cmd 可以在命令提示符下使用,npm 只能从命令行运行。兼容性:npm.cmd 特定于 Windows 系统,npm 跨平台可用。使用建议:Windows 用户使用 npm.cmd,其他操作系统使用 npm。

Node.js 中存在以下全局变量:全局对象:global核心模块:process、console、require运行时环境变量:__dirname、__filename、__line、__column常量:undefined、null、NaN、Infinity、-Infinity

Node.js 和 Java 的主要差异在于设计和特性:事件驱动与线程驱动:Node.js 基于事件驱动,Java 基于线程驱动。单线程与多线程:Node.js 使用单线程事件循环,Java 使用多线程架构。运行时环境:Node.js 在 V8 JavaScript 引擎上运行,而 Java 在 JVM 上运行。语法:Node.js 使用 JavaScript 语法,而 Java 使用 Java 语法。用途:Node.js 适用于 I/O 密集型任务,而 Java 适用于大型企业应用程序。

Node.js 项目的服务器部署步骤:准备部署环境:获取服务器访问权限、安装 Node.js、设置 Git 存储库。构建应用程序:使用 npm run build 生成可部署代码和依赖项。上传代码到服务器:通过 Git 或文件传输协议。安装依赖项:SSH 登录服务器并使用 npm install 安装应用程序依赖项。启动应用程序:使用 node index.js 等命令启动应用程序,或使用 pm2 等进程管理器。配置反向代理(可选):使用 Nginx 或 Apache 等反向代理路由流量到应用程
