node.js非管道方法读写错误问题

今天在学习node.js流的时候,参照菜鸟教程的文档写了从流中读取和写入数据的操作,但是在两者同时运行的时候,却发现写入文档并没有任何数据写入。

先上最初的错误代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//引入流模块
var fs = require('fs');
var data = '';

var readerStream = fs.createReadStream('input.txt');
var writerStream = fs.createWriteStream('output.txt');

//将文档从input.txt中读取出来存入流
readerStream.on('data', function(chunk){
console.log("1、Start:");
data += chunk;
});
readerStream.on('end', function(){
console.log("2、" + data);
});
readerStream.on('error', function(err){
console.log(err.stack);
});

//从流中读取数据写入output.txt
writerStream.write(data, 'UTF8');
writerStream.end();
writerStream.on('finish', function(){
console.log("3、Write done!");
console.log("4、All done!");
});
writerStream.on('err', function(err){
console.log(err.stack);
});

console.log("5、Program Finished!");
1
Hello world!

程序的运行结果如下:

1
2
3
4
5
5、Program Finished!
1、Start:
3、Write done!
4、All done!
2、Hello world!

这时去查看同目录下生成的output.txt是没有内容的,根据以上代码,显然在进行写入操作时,data变量还没有数据读入,因此output写入数据为空(你也可以尝试定义data为一个字符串进行测试)。

接下来对写入流(writerStream)设定一个定时器,1秒后进行操作,修改代码为:

1
2
3
4
5
6
7
8
...
setTimeout(function(){
writerStream.write(data, 'UTF8');
},1000);
setTimeout(function(){
writeStream.end();
},2000)
...

这里需要注意,在对写操作设置定时器的时候,需要对end方法也设定定时器,并且时间需要大于我们写操作定时器的时间,不然会出现以下错误:

1
2
Error: write after end
...

最终调试到以下阶段:

1
2
3
4
5
6
7
8
9
...
setTimeout(function(){
writerStream.write(data, 'UTF8');
},3);
//定时为2则抛出'Error: write after end'
setTimeout(function(){
writeStream.end();
},5)
...

可以看到从文件中读取数据至少要3ms(实际的读取时间跟文件路径以及大小有关),而程序读取完只需要不到1ms,所以才出现了最开始写入数据为空的bug。这也侧面说明了node.js的异步处理方式。

当然,其实node.js已经帮我们做好了这一堆繁杂的操作,管道流(pipe)很好的解决了这一个问题,我们的代码可以简化为:

1
2
3
4
5
6
7
var fs = require('fs');

var readerStream = fs.createReadStream('input.txt');
var writeeStream = fs.createWriteStream('output.txt');

readerStream.pipe(writeStream);
console.log("All done!");

上述的操作会使得output.txt内容被覆写,如果你只想在output.txt中追加内容,可以在定义写入流的时候追加参数:

1
var writeeStream = fs.createWriteStream('output.txt',{'flags' : 'a' });

可用的参数列表如下:

flag 说明
r 读取文件。如果文件不存在则抛出异常
r+ 读取并写入。如果文件不存在时抛出异常
rs 以同步方式读取文件并通知操作系统忽略本地文件系统缓存
w 写入文件。文件不存在则创建该文件,存在则清空文件内容
w+ 读取并写入文件。文件不存在则创建该文件,存在则清空文件内容
wx 作用与”w”类似。但以排他方式打开文件
wx+ 作用与”w+”类似。但以排他方式打开文件
a 以追加方式写入文件。如果文件不存在则创建该文件
a+ 读取并以追加方式写入文件。如果文件不存在则创建该文件
ax 作用与”a”相似。但以排他方式打开文件
ax+ 作用与”a+”相似。但以排他方式打开文件
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×