>[success] # 可读流(Readable)
| API | 描述 |
| --- | --- |
| fs.createReadStream() | 创建可读文件流 |
| http.IncomingMessage | 处理 http 请求的请求体 |
| process.stdin | 标准输入流 |
* 关于提供一些方法事件和属性
| API | 描述 |
| --- | --- |
| `readable.pipe(destination, options)` | 将可读流与可写流相连接,使可读流自动将数据推送到可写流中,通过可选的`options`参数来支持更多的连接选项。 |
| `readable.push(chunk[, encoding])` | 将新的数据块放入内部缓存队列。 |
| `readable.read([size])` | 每次触发`readable`事件或调用`read()`方法,从内部缓存队列中返回指定大小的数据块。如果没有数据块可用,则返回`null`。 |
| `readable.resume()` | 恢复可读流内部的可读状态,并触发`readable`事件。 |
| `readable.setEncoding(encoding)` | 将数据块转换为指定的字符编码。 |
| `readable.pause()` | 暂停可读流的内部可读状态,防止数据继续读取和内存占用增加。 |
| `readable.unshift(chunk)` | 将数据块放回内部缓存队列头部。 |
| `readable.wrap(stream)` | 将现有的可读流转换为 Node.js 可读流实例,以便它可以正常与可写流、管道等相互协同工作。 |
| 事件 | 描述 |
| --- | --- |
| `data` | 当内部缓存队列中有新的数据块时触发。 |
| `readable` | 当可读流准备好进行读取操作时触发。 |
| `end` | 当可读流读取完所有数据并且缓存队列为空时触发。 |
| `error` | 当发生错误时触发。 |
| 属性 | 描述 |
| --- | --- |
| `highWaterMark` | 可读流缓存队列的最大字节数,该属性影响可读流缓存的大小和数据推送的频率。 |
| `readableState` | 包含实例当前状态的对象。 |
>[info] ## fs.createReadStream(path\[, options\])
1. `fs.createReadStream`方法来**创建一个可读流,使用可读流的方式读取文件时,数据会被分成小块逐步读入内存,而不是一次性读取整个文件**,
2. 可以通过指定要读取的文件路径或者直接使用这个`fd`创建通过`fs.createReadStream`创建`ReadStream`对象
3. 通过`ReadStream`对象,**我们可以从文件中读取数据,并将其转换为我们需要的格式。例如,我们可以读取一个文本文件,并将其转换为字符串,或者读取一个二进制文件,并将其转换为Buffer对象**
4. 创建一个`ReadStream`对象时,它会自动将文件分成一块块的数据进行读取。可以通过设置`highWaterMark`属性来控制每块数据的大小。**当我们读取的数据量超过`highWaterMark`大小时,`ReadStream`对象将自动暂停读取**,等待我们处理已经读取的数据。当我们处理完数据后,`ReadStream`对象会自动恢复读取,继续读取下一块数据
5. 当我们不再需要`ReadStream`对象时,应该及时销毁它,以免造成资源浪费。销毁`ReadStream`对象后,它会自动关闭文件描述符,触发'close'事件。如果我们希望在销毁对象时不触发'close'事件,可以设置`emitClose`选项为`false`。
6. 一些优缺点对照表
| 优点 | 缺点 |
| --- | --- |
| 内存使用效率高,减少内存使用量 | 实现复杂,需要处理多个事件和回调函数 |
| 速度快,避免一次性将整个文件读入内存的延迟 | 可读性差,代码结构较复杂,可读性相对较差 |
| 数据处理灵活,可以逐块处理数据 | 不适合小文件,可能会增加代码的复杂性,不会带来很大的性能优势 |
| 可以控制读取的范围,读取部分内容 | 可靠性差,可能出现数据丢失或不完整,需要处理错误和异常情况 |
>[danger] ##### api 说明
1. 默认情况下,`createReadStream`创建的可读流**会自动关闭**,`api`参数说明,当`options.autoClose`设置为(**默认为true**)`true`时,当读取流结束时会自动关闭底层文件描述符(file descriptor)或文件句柄(file handle),也就是说,当 **读取流读取完所有数据后,底层的文件描述符或文件句柄将被自动关闭**
2. `options.emitClose`设置为(**默认为true**)`true`时,读取流会在底层文件描述符或文件句柄关闭时触发 'close' 事件,这个事件触发后,读取流的 'close' 事件也会被触发。如果`options.emitClose`设置为`false`,则不会触发 'close' 事件。
3. `options.emitClose`设置为`false`,当读取流结束时,底层的文件描述符或文件句柄仍然会被自动关闭,除非`options.autoClose`设置为`false`。
4. `options.fs`参数是一个可选的文件系统对象,这个参数通常用于测试或开发环境中后续用到在了解
| 参数名 | 类型 | 描述 | 默认值 |
| --- | --- | --- | --- |
| path | string | Buffer | URL |
| options | string | Object | 可选对象,可以包含以下属性 |
| options.flags | string | 请参阅支持的文件系统标志 | 'r' |
| options.encoding | string | 编码格式 | null |
| options.fd | integer | FileHandle | 文件描述符 `const readStream = fs.createReadStream(null, { fd })` |
| options.mode | integer | 文件的权限掩码 | 0o666 |
| options.autoClose | boolean | 自动关闭标志 | true |
| options.emitClose | boolean | 关闭事件标志 | true |
| options.start | integer | 起始位置(以字节为单位) | 0 |
| options.end | integer | 结束位置(以字节为单位) | Infinity |
| options.highWaterMark | integer | 每次读取的最大字节数 | 64 \* 1024 |
| options.fs | Object | null | 文件系统对象 |
>[danger] ##### 案例
| **事件** | **描述** |
| --- | --- |
| `data` | 当流读取到数据时触发。 |
| `end` | 当流读取完所有数据时触发。 |
| `error` | 当流发生错误时触发。 |
~~~
const fs = require('fs')
// 1. 获取readStream 流
const stream = fs.createReadStream('./b.txt')
console.log(12)
// 2. 监听流的data事件
stream.on('data', (chunk) => {
console.log(chunk.toString())
})
// 3. 监听流的end事件
stream.on('end', () => {
console.log('end')
})
// 4. 监听流的error事件
stream.on('error', (err) => {
console.log(err)
})
// 5. 监听流的close事件
stream.on('close', () => {
console.log('close')
})
// 6. 监听流的open事件
stream.on('open', () => {
console.log('open')
})
~~~
* 使用 fd 读取文件
~~~
const fs = require('fs')
const fd = fs.openSync('./a.txt')
// 1. 获取readStream 流
const stream = fs.createReadStream(null, { fd, highWaterMark: 3 })
console.log(12)
// 2. 监听流的data事件
stream.on('data', (chunk) => {
console.log(chunk.toString())
})
// 3. 监听流的end事件
stream.on('end', () => {
console.log('end')
})
// 4. 监听流的error事件
stream.on('error', (err) => {
console.log(err)
})
// 5. 监听流的close事件
stream.on('close', () => {
console.log('close')
})
// 6. 监听流的open事件
stream.on('open', () => {
console.log('open')
})
~~~
>[danger] ##### 案例二
| **方法** | **描述** |
| --- | --- |
| `pause()` | 用于暂停可读流的数据读取,调用该方法后,可读流将不会再触发 'data' 事件,直到调用`resume()`方法恢复数据读取。该方法通常用于暂停数据读取,以便进行某些操作或等待其他事件发生。 |
| `resume()` | 用于恢复可读流的数据读取,调用该方法后,可读流将继续触发 'data' 事件,从而读取数据。该方法通常用于恢复数据读取,以便继续处理数据。 |
| `pipe()` | 用于将一个**可读流**连接到一个**可写流**,将**可读流中的数据输出到可写流中**。该方法会自动管理数据流动,从而实现高效的数据传输。该方法还可以接收一个可选的参数,用于控制数据传输的方式和行为,例如是否自动关闭可写流、是否允许多个可写流等 |
| `unpipe()` | 用于停止将可读流中的数据输出到另一个可写流中,取消数据传输。可以将一个可写流对象作为参数传递给该方法,表示只取消将指定的可写流对象中的数据输出,如果不传递参数,则表示取消所有的数据输出。该方法通常用于在数据传输过程中动态取消数据输出,以便进行其他操作或处理错误。 |
* 关于方法使用,这里只是列举部分更多详细需要看`ReadStream`api
* * *
用可读流和可写流压缩文件
~~~
const fs = require('fs');
const zlib = require('zlib');
const readableStream = fs.createReadStream('source.txt');
const gzipStream = zlib.createGzip();
const writableStream = fs.createWriteStream('destination.txt.gz');
readableStream.pipe(gzipStream).pipe(writableStream);
~~~
使用`fs`模块的`createReadStream`方法创建可读流对象`readableStream`,并将其指向源文件`source.txt`。然后,我们在`data`事件中调用`pause`方法暂停数据读取,并在 1 秒钟后调用`resume`方法恢复数据读取
~~~
const fs = require('fs');
const readableStream = fs.createReadStream('source.txt');
readableStream.on('data', (chunk) => {
console.log(chunk.toString());
readableStream.pause();
setTimeout(() => {
readableStream.resume();
}, 1000);
});
~~~
使用 unpipe 方法取消数据输出,`fs`模块的`createReadStream`方法创建可读流对象`readableStream`,并将其指向源文件`source.txt`。同时,我们使用`createWriteStream`方法创建两个可写流对象`writableStream1`和`writableStream2`,并将其分别指向两个目标文件`destination1.txt`和`destination2.txt`。然后,我们使用`pipe`方法将可读流和两个可写流连接起来,将源文件中的数据拷贝到两个目标文件中。最后,我们在 1 秒钟后调用`unpipe`方法取消了其中一个数据输出流的连接,即将可读流和`writableStream1`的连接断开,使得数据只输出到`writableStream2`中
~~~
const fs = require('fs');
const readableStream = fs.createReadStream('source.txt');
const writableStream1 = fs.createWriteStream('destination1.txt');
const writableStream2 = fs.createWriteStream('destination2.txt');
const pipe1 = readableStream.pipe(writableStream1);
const pipe2 = readableStream.pipe(writableStream2);
setTimeout(() => {
readableStream.unpipe(pipe1);
}, 1000);
~~~
- 基础
- 什么是Node.js
- 理解 I/O 模型
- 理解node 中 I/O
- 对比node 和java 使用场景
- node 模块管理
- 内置模块 -- buffer
- 内置模块 -- fs
- fs -- 文件描述符
- fs -- 打开文件 api
- fs -- 文件读取 api
- fs -- 文件写入 api
- fs -- 创建目录 api
- fs -- 读取文件目录结构 api
- fs -- 文件状态(信息) api
- fs -- 删除文件/目录 api
- fs -- 重命名 api
- fs -- 复制文件 api
- 内置模块 -- events
- 内置模块 -- stream
- 可读流 -- Readable
- 可写流 -- Writable
- Duplex
- Transform
- 内置模块 -- http
- http -- 从客户端发起
- http -- 从服务端发起
- 内置模块 -- url
- 网络开发