可写流的使用

可写流的创建

Node.js 中的可写流使用 stream 模块中的 Writable 类表示,使用该类时,需要重写其中的 write() 方法,因此要使用 stream 模块中的 Writable 类创建可写流,需要使用的代码类似下面代码:

const stream = require('stream');
const writable = new stream.Writable({
     write: function (chunk, encoding, next) {
          console.log(chunk.toString());
          next();
     }
});

另外,还可以使用 fs 模块的 createWriteStream() 方法创建可写流,语法格式如下:

fs.createWriteStream(path[, options])
  • path:写入文件的文件路径。

  • options:写入文件时的可选参数,可选值如下。

    • flags:指定文件系统的权限,默认为 w,如果要修改文件内容,而不是替换,需要将该值设置为 a

    • encoding:编码方式,默认为 null

    • fd:文件描述符,默认为 null

    • mode:设置文件模式,默认为 0o666

    • autoClose:文件出错或者结束时,是否自动关闭文件,默认为 true

    • emitClose:流销毁时,是否发送 close 事件,默认为 true

    • start:指定开始写入的位置。

    • highWaterMark:可写入的阈值,一般设置在 16~100KB 范围内。

例如,下面代码创建一个可写流:

var fs = require("fs");
var write = fs.createWriteStream('demo.txt');  //创建可写流

可写流的属性方法及事件

可写流提供了很多属性、方法和事件,用来获取可写流信息、对可写流进行操作,以及监听可写流的相应操作,它们的说明分别如表10.4、表10.5和表10.6所示。

image 2024 04 13 23 45 35 879
Figure 1. 表10.4 可写流的常用属性及说明
image 2024 04 13 23 46 00 468
Figure 2. 表10.5 可写流的常用方法及说明
image 2024 04 13 23 46 25 554
Figure 3. 表10.6 可写流的常用事件及说明

可写流的常见操作

写入数据

使用 write() 方法可以向流中写入数据,其语法格式如下:

对象名.write( chunk[, encoding, callback])
  • chunk:要写入的数据,其值可以是字符串、缓冲区或数组等。

  • encoding:可选参数,表示写入数据时的编码方式。

  • callback:可选参数,是一个回调函数,写入数据完成后执行。

【例10.3】使用可写流为文件追加内容。(实例位置:资源包\源码\10\03)

使用 fs 模块的 createWriteStream 方法创建一个可写流对象,然后使用其 write 方法为古诗《凉州词》追加诗词赏析内容。代码如下:

const fs = require("fs")
var txt = "这首诗抓住了边塞风光景物的一些特点,借其严寒春迟及胡笳声声来写战士们的心理活动,反映了边关将士的生活状
况。诗风苍凉悲壮,但并不低沉,以侠骨柔情为壮士之声,这仍然是盛唐气象的回响。"
//在文件原有内容后面追加内容,所以定义文件权限为“a”
var decr = fs.createWriteStream("凉州词.txt", {flags: "a"})
decr.write("\n鉴赏:\n" + txt, "utf8")                     //写入内容

本实例的运行效果可参考图10.3和图10.4。

设置编码方式

使用 setDefaultEncoding() 方法可以设置可写流的默认编码方式,其语法格式如下:

对象名.setDefaultEncoding(encoding)

参数 encoding 表示要设置的编码方式。

例如,创建可写流,并将写入数据的编码方式设置为 utf8,代码如下:

const fs = require("fs")
var writeSteam = fs.createWriteStream("demo.txt")
writeSteam.setDefaultEncoding("utf8")              //设置编码方式
writeSteam.write("测试数据")                       //写入内容

关闭流

写入流的 end() 方法用来标识已经没有需要写入流中的数据了,因此通常用来关闭流,其语法格式如下:

对象名.end([chunk[, encoding]][, callback])
  • chunk:可选参数,表示关闭流之前要写入的数据。

  • encoding:如果 chunk 为字符串,那么 encoding 为编码方式。

  • callback:流结束或者报错时的回调函数。

例如,下面代码中在关闭流之前写入一段数据:

const fs = require("fs")
var writeSteam = fs.createWriteStream("demo.txt")
writeSteam.setDefaultEncoding("utf8")              //设置编码方式
writeSteam.write("测试数据")                       //写入内容
writeSteam.end("写入完成")                         //关闭流
//writeSteam.write('继续写入');

运行程序,demo.txt 文件中内容如下:

测试数据
写入完成

使用 end() 方法关闭流后,无法再向流中写入数据,否则将会产生异常,例如,去掉上面代码中最后一行的注释,再次运行时,将会出现如图10.5所示的错误提示。

image 2024 04 14 00 06 15 742
Figure 4. 图10.5 关闭流后继续写入数据时的错误提示

销毁流

使用 destroy() 方法可以销毁所创建的写入流,并且流被销毁后,无法再向流写入数据。其语法格式如下:

对象名.destroy([error])

参数 error 为可选参数,表示使用 error 事件触发的错误。

例如,下面代码使用写入流向一个文件中写入了一个字符串,使用 destroy() 方法销毁创建的写入流,代码如下:

const fs = require("fs")
var writeSteam = fs.createWriteStream("demo.txt")
writeSteam.setDefaultEncoding("utf8")              //设置编码方式
writeSteam.write("测试数据")                       //写入内容
writeSteam.destroy()                               //销毁流

上面代码运行后,将会导致 demo.txt 文件中没有任何数据,因为虽然第 4 行代码中使用 write() 方法写入了数据,但由于紧接着销毁了写入流,这将导致使用该流执行的任何操作都会失效。因此,在使用写入流销毁操作时,通常在异常处理中使用该操作。

一旦流被销毁,就无法对其进行任何操作,并且销毁流时,使用 write() 方法写入的数据可能并没有完成使用,这可能触发 ERR_STREAM_DESTROYED 错误,因此如果数据在关闭之前需要刷新,建议使用 end() 方法而不是 destroy() 方法。

将数据缓冲到内存

使用写入流的 cork() 方法可以强制把所有写入的数据都缓冲到内存中,它的主要目的是为了适应将几个数据快速连续地写入流的情况。cork() 方法不会立即将它们转发到底层目标处,而是缓冲所有数据块,直到调用 uncork() 方法。cork() 方法的语法格式如下:

对象名.cork()

当使用 uncork() 方法或 end() 方法时,缓冲区数据将被刷新。

例如,下面代码创建一个写入流,并在控制台中输出一句话,然后调用 cork() 方法后,在控制台中输出另外一句话,代码如下:

const stream = require('stream');
const writable = new stream.Writable({
     write: function (chunk, encoding, next) {
          console.log(chunk.toString());
          next();
     }
});
writable.write('天气晴朗');
writable.cork();
writable.write('阳光明媚');

运行结果如下:

天气晴朗

通过观察上面结果,发现调用 cork() 方法后,接下来要输出的内容并没有显示。

输出缓冲后的数据

前面介绍了 cork() 方法,用以强制把所有写入的数据都缓冲到内存中,而使用 uncork() 方法可以将调用 cork() 方法后缓冲的所有数据输出到目标处。uncork() 方法的语法格式如下:

对象名.uncork()

例如,修改上面的示例,在其最后代码下方添加一行代码,调用写入流的 uncork() 方法,即代码修改如下:

const stream = require('stream');
const writable = new stream.Writable({
     write: function (chunk, encoding, next) {
          console.log(chunk.toString());
          next();
     }
});
writable.write('天气晴朗');
writable.cork();
writable.write('阳光明媚');
writable.uncork();

运行上面代码,效果如下:

天气晴朗
阳光明媚