使用fs.createReadStream()和EISDIR同时进行`open`和`error`

时间:2014-06-24 02:45:58

标签: javascript node.js stream

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

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 的优雅方式是什么,而无需使用{{fd进行明确测试1}}是目录?还有其他类似案例吗?

2 个答案:

答案 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的回调onReadlink 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)

我已采用以下方法结束:

  1. fs.open(path, 'r', function(err, fd) { ... })获取文件描述符fd;
  2. 给定文件描述符
  3. fs.fstat(fd, function(err, stats) { ... })stat;
  4. stats.isDirectory()处理EISDIR案件;
  5. stream = fs.createReadStream(null, { fd: fd, flags: 'r' })最终获得可读文件流;
  6. 这样我保护自己免受可能与目录交换文件的影响,反之亦然,在它被检查之后但在打开之前,即使有人确实欺骗它的文件描述符仍然有效。