异步和递归目录扫描,用于 Nodejs 和 Expressjs 中的文件列表



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

实际上我可以控制台.log文件在这里你可以看到 A 标记,但是一旦异步内容完成,我就找不到将整堆完整的路径发送到视图的方法。

一些帮助将不胜感激。

这是数据 ./数据结构:

--- 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;
如果你承诺你

正在使用的fs函数,以便所有异步逻辑都是承诺,然后使用 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模块有一个更新的(仍在实验性的(基于 promise 的 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()

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

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

现在可以将筛选器添加到流中,以将结果减少到筛选规则中。在此示例中,仅接受降价文件。

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.");

只需启用控制台.log行即可查看在哪个阶段处理的文件名。

基于这个答案,这里有一个答案,它使用 fs.promises 和 ES 模块 ( import ( 语法:

import fs from "fs";
import path from "path";
export async function file_list(directory_name, results = []) {
    let files = await fs.promises.readdir(directory_name, {withFileTypes: true});
    for (let f of files) {
        let full_path = path.join(directory_name, f.name);
        if (f.isDirectory()) {
            await file_list(full_path, results);
        } else {
            results.push(full_path);
        }
    }
    return results;
}

最新更新