同时使用fs.createReadStream()和EISDIR同时打开和错误

在实现简单的静态服务中间件时,我偶然发现了下面的代码:

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)); }); 

当参数是目录时, openerror事件都会被发出!

 $ node test.js opened! 11 error! { [Error: EISDIR, read] errno: 28, code: 'EISDIR' } 

我以为我有openerror ,从来没有在同一时间。

什么是优雅的方式来区分好的open坏的open而不诉诸明确testingfdstat是目录? 还有其他类似的情况吗?

当您在Node中创build可读stream时,代码将尝试打开文件并读取内容(如果未提供fd )。 你可以通过代码看到:

调用createReadStream()创build一个ReadStream对象,如果fd不是数字( 链接到源 ),将调用this.open() ):

 if (!util.isNumber(this.fd)) this.open(); 

这调用fs.open ,我猜猜目录上打开目录? (这有点奇怪,为什么允许这样做没有错误,但代码最终击中本地绑定层,并继续。)打开它,代码发出“打开”事件,并开始读取的内容该文件( 链接到源 ):

 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的callbackonRead ( 链接到源 ):

 function onread(er, bytesRead) { if (er) { if (self.autoClose) { self.destroy(); } self.emit('error', er); 

因此,因为createReadStream调用立即打开并开始读取文件的内容(如果文件描述符未被传递),事件将按照您所看到的发送。 首先open事件,然后试图读取文件,失败,并发出error事件。

由于这是一个可读的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)); }); 

在testing中,你会在文件上点击data ,并在目录上error

我已经结束了以下方法:

  1. fs.open(path, 'r', function(err, fd) { ... })获取文件描述符fd ;
  2. fs.fstat(fd, function(err, stats) { ... })给定给定的文件描述符;
  3. stats.isDirectory()来处理EISDIR情况;
  4. stream = fs.createReadStream(null, { fd: fd, flags: 'r' })最终获得可读的文件stream;

通过这种方式,我可以保护自己免受与目录之间的交换文件的影响,反之亦然,因为即使有人做了欺骗,其文件描述符仍然有效。