异步和递归目录扫描,用于在Node.js和Express.js中列出文件

时间:2019-06-13 14:59:43

标签: node.js express asynchronous es6-promise fs

在这个 Expressjs 路由文件中,我试图(递归)获取 ./ data 目录中的所有JSON文件。

实际上,我可以 console.log 该文件,在这里您可以看到 A Mark ,但是我找不到将完整的路径发送到异步工作完成后,视图就会出现。

我们将不胜感激。

这是数据 ./ data 结构:

--- dir1
    `-- json1.json
    `-- json2.json
--- dir2
    `-- json3.json
--- dir3
const express = require('express'),
    router = express.Router(),
    fs = require('fs'),
    path = require('path')
    ;

let scan = function (directoryName = './data') {

    return new Promise((resolve, reject) => {

        fs.readdir(directoryName, function (err, files) {
            if (err) reject(err);

            files.map((currentValue, index, arr) => {
                let fullPath = path.join(directoryName, currentValue);

                fs.stat(fullPath, function (err, stat) {
                    if (err) reject(err);

                    if (stat.isDirectory()) {
                        scan(fullPath);
                    } else {
                        console.log(currentValue); <= (A mark)
                        //resolve();
                    }
                });
            });
        });
    })
};


router.get('/', (req, res, next) => {
  scan()
        .then(data => res.render('list', {
            title: 'List',
            data: data
        }))
        .catch(next);
});

module.exports = router;

3 个答案:

答案 0 :(得分:1)

如果您承诺要使用的fs函数可以简化一堆任务,以便所有异步逻辑都是Promise,然后使用async / await帮助您序列化控制流。

这是一种方法:

const promisify = require('util').promisify;
const path = require('path');
const fs = require('fs');
const readdirp = promisify(fs.readdir);
const statp = promisify(fs.stat);

async function scan(directoryName = './data', results = []) {
    let files = await readdirp(directoryName);
    for (let f of files) {
        let fullPath = path.join(directoryName, f);
        let stat = await statp(fullPath);
        if (stat.isDirectory()) {
            await scan(fullPath, results);
        } else {
            results.push(fullPath);
        }
    }
    return results;
}

以上代码已在节点v10.14.1中进行了测试。

然后可以使用与以前相同的方式:

router.get('/', (req, res, next) => {
  scan().then(data => res.render('list', {
      title: 'List',
      data: data
   })).catch(next);
});

仅供参考,fs模块有一个更新的(仍处于试验阶段)基于承诺的API。您可以这样使用:

const path = require('path');
const fsp = require('fs').promises;

async function scan2(directoryName = './data', results = []) {
    let files = await fsp.readdir(directoryName, {withFileTypes: true});
    for (let f of files) {
        let fullPath = path.join(directoryName, f.name);
        if (f.isDirectory()) {
            await scan2(fullPath, results);
        } else {
            results.push(fullPath);
        }
    }
    return results;
}

请注意,此新版本还使用了新的withFileTypes选项,省去了在每个文件上调用stat()的麻烦。

答案 1 :(得分:0)

为此有一个NPM模块:

npm dree

它允许您以同步或异步方式将目录树表示为字符串或对象。使用文件回调,您也可以实现目标。如果需要,还可以指定要考虑的文件扩展名。

代码如下:

const dree = require('dree');
const path = './myFolder';
const options = { 
    extensions: [ 'json' ], // Only json files are considered
    normalize: true,        // If you want all the paths to use only / (slashes) and not \ (backslashes) on windows
    followLinks: true       // If you want to follow and inspect even symbolic links to other directories
}
// In case you want the absolute paths of the json files
const paths = [];
// In case you want the relative paths of the json files
const relativePaths = [];
const fileCallback = function(fileNode) {
  paths.push(fileNode.path);
  relativePaths.push(fileNode.relativePath);
}

dree.scanAsync(path, options, fileCallback)
   .then(tree => {
       // paths contains all the paths you want
       // relativePaths the same but the paths are relative
       // tree contains an object representing all the folder tree including only json files
    });

使用异步并等待:

const dree = require('dree');
const path = './myFolder';
const options = { 
    extensions: [ 'json' ], // Only json files are considered
    normalize: true,        // If you want all the paths to use only / (slashes) and not \ (backslashes) on windows
    followLinks: true       // If you want to follow and inspect even symbolic links to other directories
}
// In case you want the absolute paths of the json files
const paths = [];
// In case you want the relative paths of the json files
const relativePaths = [];
const fileCallback = function(fileNode) {
  paths.push(fileNode.path);
  relativePaths.push(fileNode.relativePath);
}

async function execute() {
   const tree = await dree.scanAsync(path, options, fileCallback)
   // paths contains all the paths you want
   // relativePaths the same but the paths are relative
   // tree contains an object representing all the folder tree including only json files
}

execute();

答案 2 :(得分:0)

以上所有示例在处理找到的条目之前都会创建一个大结果数组。

这是一种将给定目录和子目录的所有找到的文件条目“流式传输”到迭代器中的解决方案。

现在可以将过滤器添加到流中,以将结果减少到过滤器规则中。 在此示例中,仅接受markdown文件。

const fsp = require('fs').promises;
const path = require('path');

// scan the directory recursively and push each filename into the iterator.
async function* scan3(dir) {
  const entries = await fsp.readdir(dir, { withFileTypes: true });
  for (const de of entries) {
    const res = path.resolve(dir, de.name);
    // console.log('>' + res);
    if (de.isDirectory()) {
      yield* scan3(res);
    } else {
      yield res;
    }
  }
}


// get all filenames from the iterator param
// and push each filename with valid extension into the resulting iterator.
async function* filterExt(it, ext) {
  for await (const e of it) {
    if (e.endsWith(ext)) {
      // console.log('>>' + e);
      yield e;
    }
  }
}


async function main() {
  const it_files = scan3('.')
  const it_mdFiles = filterExt(it_files, '.md');

  for await (const f of it_mdFiles) {
    console.log('>>>' + f);
  }
}

main();

console.log("done.");

只需启用console.log行即可查看在哪个阶段处理了什么文件名。