读取多个目录中的文件,使用 Node 和 Promises 将文件名与其数据匹配

Posted

技术标签:

【中文标题】读取多个目录中的文件,使用 Node 和 Promises 将文件名与其数据匹配【英文标题】:Reading files in multiple directories, matching filenames with their data using Node and Promises 【发布时间】:2015-07-08 23:58:37 【问题描述】:

我有一个目录数组。

var directories = ['/dir1', '/dir2'];

我想读取这些目录下的所有文件,最后有一个将文件名与其 base64/utf8 数据匹配的对象。就我而言,这些文件是图像。生成的对象可能如下所示:

var result = 
 '/dir1': 
  'file1': 'iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAIAAACx0UUtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWF...',
  'file2': 'iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAIAAACx0UUtAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWF...'   
 

我通过回调地狱轻松实现了这一点,但是当我尝试使用 Promises 时,我不确定如何将目录信息和文件名传递给后续的 then() 和 map() 函数。

在下面的示例中,我使用的是 Bluebird 库:

const Promise = require('bluebird'),
  fs = Promise.promisifyAll(require('fs'));

var getFiles = function (dir) 
      return fs.readdirAsync(dir);
  ;

Promise.map(directories, function(directory) 
    return getFiles(directory)
  ).map(function (files) 
    // which directory's files are these?
    return files;
  )

下一步是遍历文件并读取它们的数据。

我不介意答案是 ES6、Bluebird 还是 Q。

【问题讨论】:

为什么在数组上使用_.keys(directories) @Bergi 打错了,抱歉,现在已编辑。 @DenizOzger 你对递归路径解析和嵌套目录感兴趣吗? @BenjaminGruenbaum 是的,只要我可以将文件数据映射到文件名并将文件名映射到目录,什么都可以。 @DenizOzger 你用的是什么版本的node/io? 【参考方案1】:

我不知道 Bluebird,但这是 ES6 Promise 方法。

var fs = require('fs');
var directories = ['/dir1', '/dir2'];
var result = ;
Promise.all(directories.map(function(dir) 
  result[dir] = ;
  return new Promise(function(resolve, reject) 
    fs.readdir(dir, function(er, files) 
      if (er) 
        return reject(er);
      
      resolve(files);
    );
  ).then(function(files) 
    return Promise.all(files.map(function(file) 
      return new Promise(function(resolve, reject) 
        fs.readFile(dir + '/' + file, function(er, data) 
          if (er) 
            return reject(er);
          
          result[dir][file] = data.toString('base64');
          resolve();
        );
      );
    ));
  );
)).then(function() 
  console.log(result);
).catch(function(er) 
  console.log(er);
);

【讨论】:

不要使用promise constructor antipattern! 非常感谢@Tresdin 的回答,我了解它的工作原理并欣赏 ES6 方法,但我有点失望,因为它看起来像是我试图避免的回调地狱。 @DenizOzger 是的,我也是。我实际上更喜欢async 模块,因为它更快更容易阅读。 :) 哇,我很高兴我现在正在使用 bluebird。蓝鸟更好。【参考方案2】:

使用 Bluebird 最简单的方法是使用 props:

function getFiles(paths)  // an array of file/directory paths
    return Promise.props(paths.reduce(function (obj, path) 
        obj[path.split("/").pop()] = fs.statAsync(path).then(function (stat) 
            if (stat.isDirectory())
                return fs.readdirAsync(path).map(function (p) 
                    return path + "/" + p;
                ).then(getFiles);
            else if (stat.isFile())
                return fs.readFileAsync(path);
        ).error(function (e) 
            console.error("unable to read " + path + ", because: ", e.message);
            // return undefined; implied
        );
        return obj;
    , ));

【讨论】:

第三行有TypeError: Cannot read property 'then' of undefined。我们的 fs 定义是否相同? IE。 fs = Promise.promisifyAll(require('fs')) 是的,当然应该是fs.statAsync :-) 当然 :) 我将这个数组:['/dir1', '/dir2'] 传递给函数。在第一次执行 .reduce() 回调时,obj 为,path 为'/dir1'。第二次执行时,obj为undefined,path为'/dir2'。这最终出现在Cannot set property '/dir2' of undefinedreduce 函数不是这个吗?:developer.mozilla.org/en/docs/Web/javascript/Reference/… 我的意思是,如果在枚举过程中添加了文件/目录,它将在这里中断(如果它在统计数据中并且不存在 - 因为服务器会更改内容)。 好的,最新的代码编辑似乎已经解决了

以上是关于读取多个目录中的文件,使用 Node 和 Promises 将文件名与其数据匹配的主要内容,如果未能解决你的问题,请参考以下文章

使用node.js按顺序读取目录内所有文件的内容

[从`prom-client`模块导入`prom`,但已安装。如何“相应地导入prom-client”?

在 node.js 中读取文件和读取目录

如何使用 python 从位于同一目录中的多个 zip 文件夹中读取 csv 文件?

Node 读取 + 写入 + 路径问题

如何通过node读取excel文件内的数据