合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
>[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); ~~~