在实现简单的静态服务中间件时,我在以下代码中偶然发现了这个事实:
fs = require('fs');
util = require('util');
stream = fs.createReadStream('/');
stream.on('open', function(fd) { console.log('opened! ' + util.inspect(fd)); });
stream.on('error', function(err) { console.log('error! ' + util.inspect(err)); });
当参数是目录时,会发出open
和error
个事件!
$ node test.js opened! 11 error! { [Error: EISDIR, read] errno: 28, code: 'EISDIR' }
我认为我有open
或error
,而不是两个同时。
区分好open
和错误open
的优雅方式是什么,而无需使用{{fd
进行明确测试1}}是目录?还有其他类似案例吗?
答案 0 :(得分:2)
在节点中创建可读流时,代码将尝试打开文件并读取内容(如果未提供fd
)。你可以通过代码看到:
调用createReadStream()
会创建一个ReadStream
对象,如果this.open()
不是数字link to source,则会调用fd
:
if (!util.isNumber(this.fd))
this.open();
这会调用fs.open
,在我猜的目录上打开目录? (这有点奇怪,为什么允许它没有错误地工作,但代码最终会命中本机绑定层并继续。)打开它时,代码会发出" open" event并开始阅读文件的内容(link to source):
ReadStream.prototype.open = function() {
var self = this;
fs.open(this.path, this.flags, this.mode, function(er, fd) {
if (er) {
if (self.autoClose) {
self.destroy();
}
self.emit('error', er);
return;
}
self.fd = fd;
self.emit('open', fd);
// start the flow of data.
self.read();
});
};
在致电fs.read
时导致错误,该错误会发送到ReadStream
的回调onRead
(link to source) :
function onread(er, bytesRead) {
if (er) {
if (self.autoClose) {
self.destroy();
}
self.emit('error', er);
因为createReadStream
调用立即打开并开始读取文件的内容(如果文件描述符未通过),事件将在您看到时发出。首先是open
事件,然后尝试读取失败的文件,并发出error
事件。
由于这是readable stream,您可能需要考虑侦听data
事件而不是open
事件来判断这是否是可接受的文件,因为您正在立即阅读。类似的东西:
fs = require('fs');
util = require('util');
stream = fs.createReadStream('/');
stream.on('data', function(chunk) { console.log('data! ' + chunk.toString()); });
stream.on('error', function(err) { console.log('error! ' + util.inspect(err)); });
在测试中,您将在文件上点击data
,在目录上点击error
。
答案 1 :(得分:1)
我已采用以下方法结束:
fs.open(path, 'r', function(err, fd) { ... })
获取文件描述符fd
; fs.fstat(fd, function(err, stats) { ... })
到stat
; stats.isDirectory()
处理EISDIR
案件; stream = fs.createReadStream(null, { fd: fd, flags: 'r' })
最终获得可读文件流; 这样我保护自己免受可能与目录交换文件的影响,反之亦然,在它被检查之后但在打开之前,即使有人确实欺骗它的文件描述符仍然有效。