node学习—深入剖析文件流

一、文件流

1.什么是文件流?

1.概念

流是指数据的流动,数据从一个地方缓缓的流动到另一个地方。
在这里插入图片描述

2.流是有方向的

  1. 可读流: Readable
    数据从源头流向内存
  2. 可写流: Writable
    数据从内存流向源头
  3. 双工流:Duplex
    数据既可从源头流向内存,又可从内存流向源头

2.为什么要流

1.其他介质和内存的数据规模不一致

磁盘空间大,价格相对低;内存容量小,价格相对高。
在这里插入图片描述

2.其他介质和内存的数据处理能力不一致

磁盘读存数据相对较慢,内存读取数据相对较快。
在这里插入图片描述

3.文件流

1.文件流的创建

1.可读流

fs.createReadStream(path[, options])
创建一个文件可读流,用于读取文件内容

  	path:读取的文件路径
  	options:可选配置
  		encoding:编码方式
  		start:起始字节
  		end:结束字节
  		highWaterMark:每次读取数量
  			如果encoding有值,该数量表示一个字符数
  			如果encoding为null,该数量表示字节数
  		返回:Readable的子类ReadStream 
  		事件:rs.on(事件名, 处理函数)
  			open
  				文件打开事件
  				文件被打开后触发
  			error
  				发生错误时触发
  			close
  				文件被关闭后触发
  				可通过rs.close手动关闭
  				或文件读取完成后自动关闭
  					autoClose配置项默认为true
  			data
  				读取到一部分数据后触发
  				注册data事件后,才会真正开始读取
  				每次读取highWaterMark指定的数量
  				回调函数中会附带读取到的数据
  					若指定了编码,则读取到的数据会自动按照编码转换为字符串
  					若没有指定编码,读取到的数据是Buffer
  			end
  				所有数据读取完毕后触发
  		rs.pause()
  			暂停读取, 会触发pause事件
  		rs.resume()
  			恢复读取,会触发resume事件
const fs = require("fs");
const path = require("path");

const filename = path.resolve(__dirname,"./test.txt");
const rs = fs.createReadStream(filename,{
    encoding: "utf-8",
    highWaterMark: 1
});
rs.on("open",()=>{
    console.log("文件被打开了。");
});

在这里插入图片描述

rs.on("error",()=>{
    console.log("文件读取错误。");//例:读取了一个不存在的文件
});

在这里插入图片描述

rs.on("close",()=>{
    console.log("文件关闭了。");
});
rs.close();//这里使用手动关闭,或者可以等文件读取完毕后自动关闭。

在这里插入图片描述

rs.on("data",chunk => {
    console.log("读取到的数据:",chunk);
});

在这里插入图片描述

rs.on("end",() => {
    console.log("数据读取完毕。");
});

在这里插入图片描述

rs.on("data",chunk => {
    console.log("读取到的数据:",chunk);
    rs.pause();
});
rs.on("pause", () => {
    console.log("暂停了");
    setTimeout(() => {
      rs.resume();
    }, 1000);
});

在这里插入图片描述

rs.on("resume", () => {
  console.log("恢复了");
});

在这里插入图片描述

2.可写流

fs.createWriteStream(path[, options])
创建一个写入流

  	    path:写入的文件路径 			options
  		flags:操作文件的方式
  			w:覆盖
  			a:追加
  			其他
  		encoding:编码方式
  		start:起始字节
  		highWaterMark:每次最多写入的字节数 			返回:Writable的字类WriteStream
  		ws.on(事件名, 处理函数)
  			open
  			error
  			close
  		ws.write(data)
  			写入一组数据
  			data可以是字符串或Buffer
  			返回一个boolean值
  				true:写入通道没有被填满,接下来的数据可以直接写入,无须排队
  					
  				false:写入通道目前已被填满,接下来的数据将进入写入队列
  					
  					要特别注意背压问题,因为写入队列是内存中的数据,是有限的
  			当写入队列清空时,会触发drain事件
  		ws.end([data])
  			结束写入,将自动关闭文件
  				是否自动关闭取决于autoClose配置
  				默认为true
  			data是可选的,表示关闭前的最后一次写入 		rs.pipe(ws) 			将可读流连接到可写流 			返回参数的值 			该方法可解决背压问题

在这里插入图片描述
这里的一些参数就不多说了,与前面可读相似。
在这里插入图片描述
以及一些绑定事件。

const fs = require('fs');
const path = require('path');

const filename = path.resolve(__dirname,"./abc.txt");
const ws = fs.createWriteStream(filename,{
    encoding: 'utf-8',
    highWaterMark: 3
});
const flag = ws.write('1');
console.log(flag);

在这里插入图片描述
ws.write(data),写入一组数据,data可以是字符串或Buffer
返回一个Boolean值:
true:写入通道没有被填满,接下来的数据可以直接写入,无须排队
在这里插入图片描述
如上面代码,highWaterMark设置三个字节,传入的1占两个字节,所以表示通道未被占满,传下一个数据时会直接写入,无需排队。

const fs = require('fs');
const path = require('path');

const filename = path.resolve(__dirname,"./abc.txt");
const ws = fs.createWriteStream(filename,{
    encoding: 'utf-8',
    highWaterMark: 4
});
const flag = ws.write('雷');
console.log(flag);

在这里插入图片描述

const fs = require('fs');
const path = require('path');

const filename = path.resolve(__dirname,"./abc.txt");
const ws = fs.createWriteStream(filename,{
    encoding: 'utf-8',
    highWaterMark: 4
});
const flag = ws.write('𠮷');
console.log(flag);

在这里插入图片描述
在这里就可以看出,对于中文文字,有些是3个字节,有些是两个字节。
在这里插入图片描述
所以当通道的容量小于传入的数据大小,则会将后面的数据放在写入队列。
由于内存的读写速度高于磁盘的读写速度,一段时间后写入队列会不堪重负。(就是背压问题)

const fs = require('fs');
const path = require('path');

const filename = path.resolve(__dirname,"./abc.txt");
const ws = fs.createWriteStream(filename,{
    encoding: 'utf-8',
    flags: 'w',
    highWaterMark: 4
});
let i = 0;
function write(){
    let flag = true;
    while(i < 1024*1024*1 && flag){
        flag = ws.write("a");
        i ++;
    }
}
write();

在这里插入图片描述
如上方法,一旦通道饱和,即不再写入,那么如何做到将1M的内容完成呢?
这里node提供了一个事件:当写入队列清空时,会触发drain事件

function write(){
    let flag = true;
    while(i < 1024*1024*1 && flag){
        flag = ws.write("a");
        i ++;
    }
}
write();
ws.on("drain",()=>{
    write();
});

在这里插入图片描述
最后耗时20s将1M空间写满a。

2.利用文件流复制文件

方法一

const fs = require("fs");
const path = require("path");

async function method1() {
  const from = path.resolve(__dirname, "./abc.txt");
  const to = path.resolve(__dirname, "./abc2.txt");
  console.time("方式1");
  const content = await fs.promises.readFile(from);
  await fs.promises.writeFile(to, content);
  console.timeEnd("方式1");
  console.log("复制完成");
}
method1()

在这里插入图片描述
方法二

async function method2(){
    const from = path.resolve(__dirname, "./abc.txt");
    const to = path.resolve(__dirname, "./abc3.txt");
    console.time('方式二');
    const rs = fs.createReadStream(from);
    const ws = fs.createWriteStream(to);
    rs.on("data",chunk=>{
        const flag = ws.write(chunk);
        if(!flag){
            //表示下一次写入,会造成背压
            rs.pause();
        }
    });
    ws.on("drain",()=>{
        //可以继续写了
        rs.resume();
    });
    rs.on("close",()=>{
        ws.end();//完毕写入流
        console.timeEnd("方式二");
        console.log("复制完成");
    })
}
method2();

在这里插入图片描述

3.rs.pipe(ws)

将可读流连接到可写流,返回参数的值,该方法可解决背压问题。

const fs = require("fs");
const path = require("path");
async function method2() {
    const from = path.resolve(__dirname, "./abc.txt");
    const to = path.resolve(__dirname, "./abc4.txt");
    console.time("方式2");
 
    const rs = fs.createReadStream(from);
    const ws = fs.createWriteStream(to);
  
    rs.pipe(ws);
    rs.on("close", () => {
      console.timeEnd("方式2");
    });
  }
  
  method2();

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞羽逐星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值