node中的Buffer/fs/Stream的一些常用api

目录

  • 1 Buffer

    • 1.1 概述
    • 1.2 常用api
  • 2 fs 模块

  • 2.1 fs概述

  • 2.2 常用api

  • 3 stream的概念以及分类

    • 3.1 stream的常用api
    • 3.2 stream的原理

二进制缓存区[Buffer]

概述

为了让javascript能够处理二进制数据,node封装了一个Buffer类,主要用于操作字节,处理二进制数据。Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。 Buffer 的大小在被创建时确定,且无法调整。

    // Buffer类是全局变量,不需要require
    
    // 创建 buffer
    
     const buffer1 = new Buffer.alloc(10); // buffer1是一个长度为10 且用0填充的Buffer
     const buffer2 = new Buffer.alloc(10, 30); // buffer2是一个长度为10 且用30来填充的Buffer
     
     const buffer3 = new Buffer.allocUnsafe(10); // 创建一个长度为10 且未初始化的Buffer
    
    /** 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer
     创建一个长度为 10、且未初始化的 Buffer。
     这个方法比调用 Buffer.alloc() 更快,
     但返回的 Buffer 实例可能包含旧数据,
     因此需要使用 fill() 或 write() 重写。
     */
    const buffer4 = new Buffer.from([1, 2, 3]); 
    
    const buffer5 = new Buffer.from('tést'); // 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。

    const buffer6 = new Buffer.from('tést', 'latin1'); // 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
复制代码

api

类方法以及属性
  1. Buffer.alloc(size[, fill[, encoding]]): 分配一个大小为size字节的新建的Buffer.fill如果为undefined,默认用 0 填充。 如果fill的值是字符串,则encoding是fill的字符编码格式,默认值为utf8。 size必须大于等于0,且小于buffer.constants.MAX_LENGTH,否则会报异常。
  2. Buffer.allocUnsafe(size): 分配一个大小为 size 字节的新建的 Buffer 。
  3. Buffer.byteLength(string[, encoding]): 返回一个字符串的实际字节长度。
```
    const str = '\u00bd + \u00bc = \u00be';
    // 输出: ½ + ¼ = ¾: 9 个字符, 12 个字节
    console.log(`${str}: ${str.length} 个字符, ` +
            `${Buffer.byteLength(str, 'utf8')} 个字节`);
```
复制代码
  1. Buffer.compare(buf1, buf2): 比较buf1和buf2的大小,相等返回0,小于返回-1,大于返回1。相当于调用 buf1.compare(buf2)。
  2. Buffer.concat(list[, totalLength]): 返回一个合并了 list 中所有 Buffer 实例的新建的 Buffer 。
```
        const buf1 = Buffer.alloc(10);
        const buf2 = Buffer.alloc(14);
        const buf3 = Buffer.alloc(18);
        const totalLength = buf1.length + buf2.length + buf3.length;
        console.log(totalLength); // 输出: 42
        const bufA = Buffer.concat([buf1, buf2, buf3], totalLength); 
        console.log(bufA); // 输出: <Buffer 00 00 00 00 ...>
        console.log(bufA.length); // 输出: 42   
```
复制代码
  1. Buffer.from(array[| buffer | string[, encoding] | object[, offsetOrEncoding[, length]]]):

6.1 array: 通过一个八位字节的 array 创建一个新的 Buffer 。如果 array 不是一个数组,则抛出 TypeError 错误。

  const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
复制代码
> 6.2 Buffer.from(buffer): 将传入的 buffer 数据拷贝到一个新建的 Buffer 实例。
>
```
    const buf1 = Buffer.from('buffer');
    const buf2 = Buffer.from(buf1);
    buf1[0] = 0x61;
    console.log(buf1.toString());
    console.log(buf2.toString());
```  
> 6.3 Buffer.from(string[, encoding]): 新建一个包含所给的 JavaScript 字符串 string 的 Buffer 。 encoding 参数指定 string 的字符编码。
> 6.4 Buffer.from(object[, offsetOrEncoding[, length]])
>
复制代码
     class Foo {
        [Symbol.toPrimitive]() {
          return 'this is a test';
        }
      }
      const buf = Buffer.from(new Foo(), 'utf8');    
复制代码
  1. Buffer.isBuffer(obj): 如果 obj 是一个 Buffer 则返回 true ,否则返回 false 。
  2. Buffer.isEncoding(encoding): 如果 encoding 是一个支持的字符编码则返回 true,否则返回 false 。
  3. Buffer.poolSize: 这是用于决定预分配的、内部 Buffer 实例池的大小的字节数。 这个值可以修改。
实例属性以及方法
  1. buf[index]: 索引操作符 [index] 可用于获取或设置 buf 中指定 index 位置的八位字节。
```
    const str = 'Node.js';
    const buf = Buffer.allocUnsafe(str.length); 
    for (let i = 0; i < str.length; i++) {
      buf[i] = str.charCodeAt(i);
    }   
    // 输出: Node.js
    console.log(buf.toString('ascii'));
```
复制代码
  1. buf.buffer: buffer 属性指向创建该 Buffer 的底层的 ArrayBuffer 对象
```
    const arrayBuffer = new ArrayBuffer(16);
    const buffer = Buffer.from(arrayBuffer);
    console.log(buffer.buffer === arrayBuffer);
    // 输出: true
```
复制代码
  1. buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]]): 比较 buf 与 target,返回表明 buf 在排序上是否排在 target 之前、或之后、或相同。 对比是基于各自 Buffer 实际的字节序列。如果 target 与 buf 相同,则返回 0 。如果 target 排在 buf 前面,则返回 1 。如果 target 排在 buf 后面,则返回 -1 。
  2. buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
```
    // 创建两个 Buffer 实例 buf1 与 buf2 ,并拷贝 buf1 中第 16 个至第 19 个字节到 buf2 第 8 个字节起。
    const buf1 = Buffer.allocUnsafe(26);
    const buf2 = Buffer.allocUnsafe(26).fill('!');
    for (let i = 0; i < 26; i++) {
      // 97 是 'a' 的十进制 ASCII 值
      buf1[i] = i + 97;
    }
    buf1.copy(buf2, 8, 16, 20);  
    // 输出: !!!!!!!!qrst!!!!!!!!!!!!!
    console.log(buf2.toString('ascii', 0, 25));
```
复制代码
  1. buf.equals(otherBuffer): 如果 buf 与 otherBuffer 具有完全相同的字节,则返回 true,否则返回 false。
  2. buf.fill(value[, offset[, end]][, encoding]): 如果未指定 offset 和 end,则填充整个 buf。 这个简化使得一个 Buffer 的创建与填充可以在一行内完成。例如: Buffer.allocUnsafe(50).fill('h');
  1. buf.includes(value[, byteOffset][, encoding]): 返回: 如果 buf 找到 value,则返回 true,否则返回 false。
  2. buf.indexOf(value[, byteOffset][, encoding]): 返回: buf 中 value 首次出现的索引,如果 buf 没包含 value 则返回 -1
  3. buf.keys(): 创建并返回一个包含 buf 键名(索引)的迭代器。
  4. buf.length: 返回 buf 在字节数上分配的内存量。 注意,这并不一定反映 buf 内可用的数据量。
  5. buf.slice([start[, end]]): 返回一个指向相同原始内存的新建的 Buffer,但做了偏移且通过 start 和 end 索引进行裁剪。
  6. buf.toJSON(): 返回 buf 的 JSON 格式。 当字符串化一个 Buffer 实例时,JSON.stringify() 会隐式地调用该函数。
  7. buf.toString([encoding[, start[, end]]]): 根据 encoding 指定的字符编码解码 buf 成一个字符串。 start 和 end 可传入用于只解码 buf 的一部分。
  8. buf.values(): 创建并返回一个包含 buf 的值(字节)的迭代器。 当 Buffer 使用 for..of 时会自动调用该函数。
  9. buf.write(string[, offset[, length]][, encoding]):根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。

fs模块

####1.1 概述

fs模块是文件操作的封装,继承了EventEmitter、stream、path等底层模块,它提供了文件读取、写入、更名、删除、遍历目录、链接等POSIX文件系统操作。与其它模块不同的是,fs模块中所有的操作都提供了异步和同步的两个版本。

文件描述符

在 POSIX 系统,内核为所有进程维护着一张当前打开着的文件与资源的表格。 每个打开的文件都会分配一个名为文件描述符的数值标识。 在系统层,所有文件系统操作使用这些文件描述符来识别与追踪每个特定的文件。 Window 系统使用了一个不同但概念类似的机制来追踪资源。 为方便用户,Node.js 抽象了不同操作系统间的差异,为所有打开的文件分配了数值的文件描述符。fs.open() 方法用于分配一个新的文件描述符。 一旦分配了,文件描述符可用于读取数据、写入数据、或查看文件信息。

    fs.open('/open/some/file.txt', 'r', (err, fd) => {
      if (err) throw err;
      fs.fstat(fd, (err, stat) => {
        if (err) throw err;
        // 各种操作
    
        // 必须关闭文件描述符!
        fs.close(fd, (err) => {
          if (err) throw err;
        });
      });
    });
复制代码
fs.ReadStream 类

成功调用 fs.createReadStream() 会返回一个新的 fs.ReadStream 对象。 fs.ReadStream 对象都是可读流。

  1. 监听的事件有: close open rendy['open'之后立即出发]
  2. readStream.bytesRead: 已读取的字节数。
  3. readStream.path: 流正在读取的文件的路径,指定在 fs.createReadStream() 的第一个参数。 如果 path 传入的是字符串,则 readStream.path 是一个字符串。 如果 path 传入的是 Buffer,则 readStream.path 是一个 Buffer。
fs.Stats 类

fs.Stats 对象提供了一个文件的信息。

  1. stats.isBlockDevice(): 如果 fs.Stats 对象表示一个块设备,则返回 true 。
  2. stats.isCharacterDevice(): 如果 fs.Stats 对象表示一个字符设备,则返回 true 。
  3. stats.isDirectory(): 如果 fs.Stats 对象表示一个文件系统目录,则返回 true 。
  4. stats.isFIFO(): 如果 fs.Stats 对象表示一个先进先出(FIFO)管道,则返回 true 。
  5. stats.isFile(): 如果 fs.Stats 对象表示一个普通文件,则返回 true 。
  6. stats.isSocket()
  7. stats.isSymbolicLink(): 如果 fs.Stats 对象表示一个符号链接,则返回 true 。
  8. stats上还有诸如dev/ino/mode/nlink/size等相关属性。
fs.WriteStream 类

WriteStream 是一个可写流。

  1. 监听的事件有: close open ready
  2. writeStream.bytesWritten: 已写入的字节数。 不包括仍在排队等待写入的数据。
  3. writeStream.path: 流正在写入的文件的路径,指定在 fs.createWriteStream() 的第一个参数。 如果 path 传入的是一个字符串,则 writeStream.path 是一个字符串。 如果 path 传入的是一个 Buffer,则 writeStream.path 是一个 Buffer。

fs上的方法

  1. fs.access(path[, mode], callback): 测试 path 指定的文件或目录的用户权限。 mode 参数是一个可选的整数,指定要执行的可访问性检查。 文件访问常量定义了 mode 可选的值。 可以创建由两个或更多个值的位或组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK)。最后一个参数 callback 是一个回调函数,会传入一个可能的错误参数。 如果可访问性检查失败,则错误参数会是一个 Error 对象。
    ```
        const file = 'package.json';
        // 检查文件是否存在于当前目录,且是否可写。
        fs.access(file, fs.constants.F_OK | fs.constants.W_OK, (err) => {
     if (err) {
            console.error(
              `${file} ${err.code === 'ENOENT' ? '不存在' : '只可读'}`);
          } else {
            console.log(`${file} 存在,且可写`);
          }
        });
    ```
复制代码
  1. fs.accessSync(path[, mode]): 同步地测试 path 指定的文件或目录的用户权限。
```
    try {
        fs.accessSync('etc/passwd', fs.constants.R_OK | fs.constants.W_OK);
        console.log('可读可写');
    } catch (err) {
        console.error('不可访问!');
    }
```  
复制代码
  1. fs.appendFile(path, data[, options], callback):异步地追加数据到一个文件,如果文件不存在则创建文件。 data 可以是一个字符串或 Buffer。注意 file 可能是一个被打开用来追加数据的数字文件描述符(通过 fs.open() 或者 fs.openSync())。这样的文件描述符将不会被自动关闭。
```
    参数
    file <string> | <Buffer> | <URL> | <number> 文件名或文件描述符
    data <string> | <Buffer>
    options <Object> | <string>
    encoding <string> | <null> 默认为 'utf8'
    mode <integer> 默认为 0o666
    flag <string> 默认为 'a'
    callback <Function>
    err <Error>
    ----------------------------------------------------------------- 
    fs.open('message.txt', 'a', (err, fd) => {
        if (err) throw err;
        fs.appendFile(fd, 'data to append', 'utf8', (err) => {
        fs.close(fd, (err) => {
            if (err) throw err;
        });
        if (err) throw err;
  });
});
```    
复制代码
  1. fs.appendFileSync(path, data[, options]): 同步的将data追加到path对应得文件中。
  2. fs.chmod(path, mode, callback): 异步地改变文件的权限。 完成回调只有一个可能的异常参数。
  3. fs.chmodSync(path, mode): 同步地改变文件的权限。 返回 undefined。 fs.chmod()的同步版本。
  4. fs.chown(path, uid, gid, callback): 异步地改变文件的所有者和群组。 完成回调只有一个可能的异常参数。
  5. fs.chownSync(path, uid, gid): 同步地改变文件的所有者和群组。 返回 undefined。 fs.chown() 的同步版本。
  6. fs.close(fd, callback):异步的关闭文件。 完成回调只有一个可能的异常参数。
  7. fs.closeSync(fd)
  8. fs.copyFile(src, dest[, flags], callback): 异步的将 src 拷贝到 dest。Asynchronously copies src to dest. 默认情况下,如果 dest 已经存在会被覆盖。回调函数没有给出除了异常以外的参数。Node.js 不能保证拷贝操作的原子性。如果目标文件打开后出现错误,Node.js 将尝试删除它。flags 是一个可选的整数,用于指定行为的拷贝操作。唯一支持的 flag 是 fs.constants.COPYFILE_EXCL ,如果 dest 已经存在,则会导致拷贝操作失败。
  9. fs.copyFileSync(src, dest[, flags]): 同步的将 src 拷贝到 dest。
  10. fs.createReadStream(path[, options]):
```
    path <string> | <Buffer> | <URL>
    options <string> | <Object>
    flags <string> 详见支持的文件系统flag。默认为 'r'。
    encoding <string> 默认为 null。
    fd <integer> 默认为 null。
    mode <integer> 默认为 0o666。
    autoClose <boolean> 默认为 true。
    start <integer>
    end <integer> 默认为 Infinity。
    highWaterMark <integer> 默认为 64 * 1024。
```
返回: <fs.ReadStream> 详见可读流。
不同于 highWaterMark 默认值为 16 kb 的可读流,该方法返回的流的 highWaterMark 默认为 64 kb。
options 可以包括 start 和 end 值,用于从文件读取一定范围的字节而不是整个文件。 start 和 end 都是包括在内的,且从 0 开始。 如果指定了 fd 且 start 不传或为 undefined,则 fs.createReadStream() 从当前文件位置按顺序地读取。 encoding 可以是任何可以被 Buffer 接受的值。
如果指定了 fd,则 ReadStream 会忽略 path 参数并且会使用指定的文件描述符。 这意味着不会触发 'open' 事件。 注意,fd 必须是阻塞的,非阻塞的 fd 应该传给 net.Socket。
 如果 autoClose 为 false,则文件描述符不会被关闭,即使有错误。 应用程序需要负责关闭它,并且确保没有文件描述符泄漏。 如果 autoClose 被设置为 true(默认),则在 error 或 end 时,文件描述符会被自动关闭。
mode 用于设置文件模式(权限和粘滞位),但仅限创建文件时。 
复制代码
  1. fs.createWriteStream(path[, options])
```
    path <string> | <Buffer> | <URL>
    options <string> | <Object>
    flags <string> 详见支持的文件系统flag。默认为 'w'。
    encoding <string> 默认为 'utf8'。
    fd <integer> 默认为 null。
    mode <integer> 默认为 0o666。
    autoClose <boolean> 默认为 true。
    start <integer>
```
返回: <fs.WriteStream> 详见可写流。
options 也有 start 选项,用于写入数据到文件指定位置。 如果是修改文件而不是覆盖它,则 flags 模式需为 r+ 模式而不是默认的 w 模式。 encoding 可以是任何可以被 Buffer 接受的值。
如果 autoClose 被设置为 true(默认),则在 error 或 finish 时,文件描述符会被自动关闭。 如果 autoClose 为 false,则文件描述符不会被关闭,即使有错误。 应用程序需要负责关闭它,并且确保没有文件描述符泄漏。
类似 ReadStream,如果指定了 fd,则 WriteStream 会忽略 path 参数并且会使用指定的文件描述符。 这意味着不会触发 'open' 事件。 注意,fd 必须是阻塞的,非阻塞的 fd 应该传给 net.Socket。
如果 options 是一个字符串,则它指定字符编码。 
复制代码
  1. fs.existsSync(path): 如果路径存在,则返回 true,否则返回 false。
  2. fs.open(path, flags[, mode], callback)与fs.openSync(path, flags[, mode]): 异步或者同步地打开文件
  3. fs.read(fd, buffer, offset, length, position, callback)与 fs.readSync(fd, buffer, offset, length, position): 从 fd 指定的文件中读取数据。
  4. fs.readdir(path[, options], callback):读取一个目录的内容。 回调有两个参数 (err, files),其中 files 是目录中不包括 '.' 和 '..' 的文件名的数组。
  5. fs.readdirSync(path[, options])
  6. fs.readFile(path[, options], callback): 异步地读取一个文件的全部内容
  7. fs.readFileSync(path[, options])
  8. fs.rename(oldPath, newPath, callback)与 fs.renameSync(oldPath, newPath): 修改文件名称
  9. fs.rmdir(path, callback)与 fs.rmdirSync(path): 删除文件目录
  10. fs.write与fs.writeFile

实在很抱歉,本来是要写一些原理性的东西,无奈,fs和buffer的基础方法和属性实在太多了,所以只能先按照官网上说了,先过一遍基础知识了。。。但我相信掌握基础的api还是是有很必要的,争取下次写点看着高级的东西

皮卡丘

Stream

概述
  • 流是一组有序的,有起点和终点的字节数据传输手段 它不关心文件的整体内容,只关注是否从文件中读到了数据,以及读到数据之后的处理
  • 流是一个抽象接口,被 Node 中的很多对象所实现。比如HTTP 服务器request和response对象都是流。
  • 可读流和可写流对文件的操作用的也是fs模块
  • node中很多内容都应用到了流,比如http模块的req就是可读流,res是可写流,而socket是可读可写流,看起来很腻害的样子~
流的分类
  • stream.Readable---用于在I/O上获取数据,如 fs.createWriteStream()

  • stream.Writable---用于在输出的目标写入数据, 如 fs.createReadStream()

  • stream.Duplex---一个可读可写的流,例如网络连接,如net.Socket

  • stream.Transform---一个会以某种方式修改数据的双工流,如 zlib.createDeflate()

缓冲区
  • 可写流和可读流都会在一个内部的缓冲器中存储数据,可以分别使用的 writable.writableBuffer 或 readable.readableBuffer 来获取。
  • 可缓冲的数据的数量取决于传入流构造函数的 highWaterMark 选项。 对于普通的流,highWaterMark 表示字节的总数量。 对于以对象模式运作的流,highWaterMark 指定了对象的总数量。
  • 当调用 stream.push(chunk) 时,数据会被缓冲在可读流中。 如果流的消费程序没有调用 stream.read(),则这些数据会停留在内部队列中,直到被消费。一旦内部的可读缓冲的总大小达到 highWaterMark 指定的阈值时,流会暂时停止从底层资源读取数据,直到当前缓冲的数据被消费 (也就是说,流会停止调用内部的用于填充可读缓冲的 readable._read() 方法)。
  • 当反复地调用 writable.write(chunk) 方法时,数据会被缓冲在可写流中。 当内部的可写缓冲的总大小小于 highWaterMark 设置的阈值时,调用 writable.write() 会返回 true。 一旦内部缓冲的大小达到或超过 highWaterMark 时,则会返回 false。
  • stream API 的主要目标,特别是 stream.pipe() 方法,是为了限制数据的缓冲到可接受的程度,也就是读写速度不一致的源头与目的地不会压垮可用的内存。
  • 因为 Duplex 和 Transform 都是可读又可写的,所以它们各自维护着两个相互独立的内部缓冲器用于读取和写入, 这使得它们在维护数据流时,读取和写入两边可以各自独立地运作。
stream.Writable 类
常用事件
  • 'close' 事件: 当流或其底层资源(比如文件描述符)被关闭时,触发 'close' 事件。 该事件表明不会再触发其他事件,且不会再发生运算。
  • 'drain'事件: 当一个流不处在 drain 的状态, 对 write() 的调用会缓存数据块, 并且返回 false。 一旦所有当前所有缓存的数据块都排空了(被操作系统接受来进行输出), 那么 'drain' 事件就会被触发 建议, 一旦 write() 返回 false, 在 'drain' 事件触发前, 不能写入任何数据块
  • 'error' 事件:当写入数据出错或使用管道出错时,触发 'error' 事件。 监听器回调函数被调用时会传入一个 Error 参数。但触发 'error' 事件时,流还未被关闭。
  • 'pipe' 事件: 当在可读流上调用 stream.pipe() 方法添加可写流到目标流向时,触发 'pipe' 事件。
  • 'unpipe' 事件: 当在可读流上调用 stream.unpipe() 方法从目标流向中移除当前可写流时,触发 'unpipe' 事件。当可读流通过管道流向可写流发生错误时,也会触发 'unpipe' 事件。
实例方法与属性

writable: stream.Writable实例

  • writable.destroy([error]):摧毁这个流,并发出传过来的错误。当这个函数被调用后,这个写入流就结束了。
  • writable.end([chunk][, encoding][, callback]):
chunk <string> | <Buffer> | <Uint8Array> | <any> 可选的,需要写入的数据。对于非对象模式下的流, chunk 必须是字符串、或 Buffer、或 Uint8Array。对于对象模式下的流, chunk 可以是任意的 JavaScript 值,除了 null。
encoding <string> 如果 chunk 是字符串,这里指定字符编码。
callback <Function> 可选的,流结束时的回调函数。
复制代码
调用 writable.end() 方法表明接下来没有数据要被写入可写流。通过传入可选的 chunk 和 encoding 参数,可以在关闭流之前再写入一段数据。如果传入了可选的 callback 函数,它将作为 'finish' 事件的回调函数。
复制代码
  • writable.setDefaultEncoding(encoding):writable.setDefaultEncoding() 用于为可写流设置 encoding。
  • writable.writableHighWaterMark: 返回构造该可写流时传入的 highWaterMark 参数值。
  • writable.writableLength:这个属性包含了写入就绪队列的字节(或者对象)数
  • writable.write(chunk[, encoding][, callback])
    chunk <string> | <Buffer> | <Uint8Array> | <any>
    encoding <string> 如果 chunk 是字符串,这里指定字符编码。
    callback <Function> 缓冲数据输出时的回调函数。
    返回: <boolean> 如果流需要等待 'drain' 事件触发才能继续写入数据,这里将返回 false ; 否则返回 true复制代码

writable.write() 方法向流中写入数据,并在数据处理完成后调用 callback 。如果有错误发生, callback 不一定 以这个错误作为第一个参数并被调用。要确保可靠地检测到写入错误,应该监听 'error' 事件。

在确认了 chunk 后,如果内部缓冲区的大小小于创建流时设定的 highWaterMark 阈值,函数将返回 true 。 如果返回值为 false ,应该停止向流中写入数据,直到 'drain' 事件被触发。 当一个流不处在 drain 的状态, 对 write() 的调用会缓存数据块, 并且返回 false。 一旦所有当前所有缓存的数据块都排空了(被操作系统接受来进行输出), 那么 'drain' 事件就会被触发。 我们建议, 一旦 write() 返回 false, 在 'drain' 事件触发前, 不能写入任何数据块。 然而,当流不处在 'drain' 状态时, 调用 write() 是被允许的, Node.js 会缓存所有已经写入的数据块, 直到达到最大内存占用, 这时它会无条件中止。 甚至在它中止之前, 高内存占用将会导致差的垃圾回收器的性能和高的系统相对敏感性 (即使内存不再需要,也通常不会被释放回系统)。 对于一个 Transform, 写入数据到一个不会drain的流尤其成问题, 因为 Transform 流默认被暂停, 直到它们被pipe或者被添加了 'data' 或 'readable' 事件处理函数。

可读流的应用

客户端上的 HTTP 请求 服务器上的 HTTP 响应 fs 写入的流 zlib 流 crypto 流 TCP socket 子进程 stdin process.stdout、process.stderr

可读流
两种模式

可读流实质上运作于流动中(flowing)或已暂停(paused)两种模式之一。

  • 在 flowing 模式中,数据自动地从底层的系统被读取,并通过 EventEmitter 接口的事件尽可能快地被提供给应用程序。

  • 在 paused 模式中,必须显式调用 stream.read() 方法来从流中读取数据片段。

所有可读流都开始于 paused 模式。 paused To flowing:

  • 新增一个 'data' 事件处理函数。
  • 调用 stream.resume() 方法。
  • 调用 stream.pipe() 方法发送数据到可写流。

flowing To paused:

  • 如果没有管道目标,调用 stream.pause() 方法。
  • 如果有管道目标,移除所有管道目标。调用 stream.unpipe() 方法可以移除多个管道目标。
三种模式

在任意时刻,任一可读流会处于以下三种状态之一:

  • readable.readableFlowing = null
  • readable.readableFlowing = false
  • readable.readableFlowing = true

当 readable.readableFlowing 为 null 时,没有提供消费流数据的机制,所以流不会产生数据。 在这个状态下,监听 'data' 事件、调用 readable.pipe() 方法、或调用 readable.resume() 方法, 则 readable.readableFlowing 会变成 true ,可读流开始主动地产生数据触发事件。

调用 readable.pause()、readable.unpipe()、或接收背压,则 readable.readableFlowing 会被设为 false,暂时停止事件流动但不会停止数据的生成。 在这个状态下,为 'data' 事件设置监听器不会使 readable.readableFlowing 变成 true。

事件
  • 'close' 事件:'close' 事件将在流或其底层资源(比如一个文件)关闭后触发。'close' 事件触发后,该流将不会再触发任何事件。

  • 'data' 事件: 'data' 事件会在流将数据传递给消费者时触发。当流转换到 flowing 模式时会触发该事件。调用 readable.pipe(), readable.resume() 方法,或为 'data' 事件添加回调可以将流转换到 flowing 模式。 'data' 事件也会在调用 readable.read() 方法并有数据返回时触发。如果调用 readable.setEncoding() 方法明确为流指定了默认编码,回调函数将接收到一个字符串,否则接收到的数据将是一个 Buffer 实例。

        const readable = getReadableStreamSomehow();
        readable.on('data', (chunk) => {
          console.log(`Received ${chunk.length} bytes of data.`);
        });
    复制代码

'end' 事件:'end' 事件将在流中再没有数据可供消费时触发。

  • 'error' 事件: 通常,这会在底层系统内部出错从而不能产生数据,或当流的实现试图传递错误数据时发生。
  • 'readable' 事件: 'readable' 事件将在流中有数据可供读取时触发。在某些情况下,为 'readable' 事件添加回调将会导致一些数据被读取到内部缓存中。通常情况下,readable.pipe() 方法和 'data' 事件机制比 'readable' 事件更容易理解。然而处理 'readable'事件可能造成吞吐量升高。
实例方法与属性
  • readable.destroy([error]):销毁流,并且触发error事件。然后,可读流将释放所有的内部资源。

  • readable.isPaused():readable.isPaused() 方法返回可读流的当前操作状态。

  • readable.pause(): readable.pause() 方法将会使 flowing 模式的流停止触发 'data' 事件, 进而切出 flowing 模式。任何可用的数据都将保存在内部缓存中。

        const readable = getReadableStreamSomehow();
        readable.on('data', (chunk) => {
          console.log(`Received ${chunk.length} bytes of data.`);
          readable.pause();
          console.log('There will be no additional data for 1 second.');
          setTimeout(() => {
            console.log('Now data will start flowing again.');
            readable.resume();
          }, 1000);
        });
    复制代码
  • readable.pipe(destination[, options]): readable.pipe() 绑定一个 [Writable][] 到 readable 上, 将可写流自动切换到 flowing 模式并将所有数据传给绑定的 [Writable][]。数据流将被自动管理。这样,即使是可读流较快,目标可写流也不会超负荷(overwhelmed)。

    destination <stream.Writable> 数据写入目标
    options <Object> Pipe 选项
        end <boolean> 在 reader 结束时结束 writer 。默认为 true。
        
    const readable = getReadableStreamSomehow();
    const writable = fs.createWriteStream('file.txt');
    复制代码

// readable 中的所有数据都传给了 'file.txt' readable.pipe(writable);

```
复制代码
  • readable.read([size]):从内部缓冲区中抽出并返回一些数据。 如果没有可读的数据,返回null。readable.read()方法默认数据将作为“Buffer”对象返回 ,除非已经使用readable.setEncoding()方法设置编码或流运行在对象模式。可选的size参数指定要读取的特定数量的字节。如果size字节不可读,将返回null除非流已经结束,在这种情况下所有保留在内部缓冲区的数据将被返回。如果没有指定size参数,则内部缓冲区包含的所有数据将返回。
  • readable.readableHighWaterMark: 返回构造该可读流时传入的 'highWaterMark' 属性。
  • readable.resume(): readable.resume() 方法会重新触发 'data' 事件, 将暂停模式切换到流动模式。
  • readable.setEncoding(encoding): 从可读流读入的数据设置字符编码
  • readable.unshift(chunk):readable.unshift() 方法会把一块数据压回到Buffer内部。 这在如下特定情形下有用: 代码正在消费一个数据流,已经"乐观地"拉取了数据。 又需要"反悔-消费"一些数据,以便这些数据可以传给其他人用。
可读流的应用

客户端上的 HTTP 响应 服务器上的 HTTP 请求 fs 读取的流 zlib 流 crypto 流 TCP socket 子进程 stdout 与 stderr process.stdin 所有的可读流都实现了 stream.Readable 类上定义的接口。

艾玛,终于弄完了,估计师傅看了,又该说我low了,?

皮卡丘

转载于:https://juejin.im/post/5b429e58f265da0f563dbc3a

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值