fs.createReadStream,fs.createWriteStream
1. 认识 Stream
什么是流呢?
- 第一反应是流水,源源不断的流动;
- 程序中的流也是类似的含义,从一个文件中读取数据时,文件的二进制(字节)数据会源源不断的被读取到程序中;
- 而这个一连串的字节,就是程序中的流;
所以,可以这样理解流:
是连续字节的一种表现形式和抽象概念;
流应该是可读的,也是可写的;
通过 readFile或者 writeFile方式读写文件,为什么还需要流呢?
- 直接读写文件的方式,虽然简单,但是无法控制一些细节的操作;
- 比如从什么位置开始读、读到什么位置、一次性读取多少个字节;
- 读到某个位置后,暂停读取,某个时刻恢复读取等等;
- 或者这个文件非常大,比如一个视频文件,一次性全部读取并不合适;
2. 文件读写的Stream
事实上Node中很多对象是基于流实现的
- http模块的Request和Response对象;
- process.stdout对象;
官方:另外所有的流都是
EventEmitter的实例
const Stream = module.exports = require('internal/streams/legacy').Stream; // internal/streams/legacy.js const EE = require('events'); function Stream(opts) { EE.call(this, opts); } ObjectSetPrototypeOf(Stream.prototype, EE.prototype); ObjectSetPrototypeOf(Stream, EE); // events.js module.exports = EventEmitter;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- Node.js中有四种基本流类型
- `Writable`:可以向其写入数据的流(例如 fs.createWriteStream())。
- `Readable`:可以从中读取数据的流(例如 fs.createReadStream())。
- `Duplex`:同时为Readable和的流Writable(例如 net.Socket)。
- `Transform`:Duplex可以在写入和读取数据时修改或转换数据的流(例如zlib.createDeflate())
## Readable
- 之前读取一个文件的信息
```js
const fs = require("fs");
fs.readFile("./a.txt",{encoding:"utf8"},(err,data)=>{
// Hello World
console.log(data);
})
这种方式是一次性将一个文件中所有的内容都读取到程序(内存)中,但是这种读取方式就会出现很多问题
- 文件过大、读取的位置、结束的位置、一次读取的大小;
这个时候,可以使用 createReadStream,更多参数可以参考官网
https://nodejs.org/dist/latest-v16.x/docs/api/fs.html#fscreatereadstreampath-options
start:文件读取开始的位置;
end:文件读取结束的位置;
highWaterMark:一次性读取字节的长度,默认是64kb;
encoding:编码方式;
2.0.1. Readable的使用
创建文件的Readable
1
2
3
4
5
6
7const fs = require("fs");
const read = fs.createReadStream("./a.txt",{
start: 3,
end: 6,
highWaterMark: 2,
encoding: "utf-8"
});可以通过监听data事件,获取读取到的数据;
1
2
3read.on("data",(data)=>{
console.log(data);
})
2.0.2. 其他操作
监听其他事件、暂停或者恢复
1 | read.on("data",(data)=>{ |
2.1. Writable
之前写入一个文件的方式是这样的:
1
2
3
4const fs = require("fs");
fs.writeFile("./b.txt","Hello",err=>{
console.log(err);
});这种方式相当于一次性将所有的内容写入到文件中,但是这种方式也有很多问题:
- 比如希望写入内容,精确每次写入的位置等;
这个时候,可以使用 createWriteStream,更多参数可以参考官网
https://nodejs.org/dist/latest-v16.x/docs/api/fs.html#fscreatewritestreampath-options
flags:默认是w,如果希望是追加写入,可以使用 a或者 a+;
start:写入的位置;
encoding:默认utf8
2.1.1. Writable的使用
进行一次简单的写入
1
2
3
4
5
6
7
8
9const write = fs.createWriteStream("./b.txt",{
flags: 'a+'
});
write.write("Hello",err=>{
if(err){
console.log(err);
}
});监听open事件
1
2
3write.on("open",()=>{
console.log("开始");
});
2.1.2. close的监听
并不能监听到 close 事件:
这是因为写入流在打开后是不会自动关闭的;
必须手动关闭,来告诉Node已经写入结束了;
并且会发出一个 finish 事件的;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const write = fs.createWriteStream("./b.txt",{
flags: 'a+'
});
write.close();
write.on("open",()=>{
console.log("开始");
});
write.on("close",()=>{
console.log("close关闭");
});
write.on("finish",()=>{
console.log("finish关闭");
});另外一个非常常用的方法是 end
end方法相当于做了两步操作:
write传入的数据和调用close方法;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const write = fs.createWriteStream("./b.txt",{
flags: 'a+'
});
write.on("open",()=>{
console.log("开始");
});
write.on("close",()=>{
console.log("close关闭");
});
write.on("finish",()=>{
console.log("finish关闭");
});
write.end("world");
2.1.3. pipe方法
正常情况下,可以将读取到的输入流,手动的放到输出流中进行写入
1
2
3
4
5
6
7
8
9
10
11const read = fs.createReadStream("./a.txt");
const write = fs.createWriteStream("./copy.txt");
read.on("data",data=>{
write.write(data,err=>{
if(err){
console.log(err);
}
})
});也可以通过pipe来完成这样的操作
1
2
3
4const read = fs.createReadStream("./a.txt");
const write = fs.createWriteStream("./copy.txt");
read.pipe(write);