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

Posted

技术标签:

【中文标题】异步和递归目录扫描,用于 Nodejs 和 Expressjs 中的文件列表【英文标题】:Async and recursive directory scan, for file listing in Nodejs and Expressjs 【发布时间】:2019-10-28 05:12:49 【问题描述】:

在这个 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;

【问题讨论】:

【参考方案1】:

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

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

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

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 行即可查看在哪个阶段处理了什么文件名。

【讨论】:

【参考方案2】:

如果您承诺您正在使用的 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;

以上代码在node 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()

【讨论】:

一个非常棒和优雅的解决方案,用 8.16.0 测试!我仍然有一个疑问:为什么需要 Promisify 包呢? Promisify 是否以同步方式执行 Readdir 和 Stat?非常感谢,我已经为此苦苦挣扎了好几个小时。 @ant0nio - util.promisify() 在遵循 node.js 异步调用约定的任何异步函数周围放置一个 promise 包装器(最后一个参数是使用参数 (err, data) 调用的回调函数。很多人都这样做了这个手动然后最后node.js添加了一个实用功能以使其更简单.如果你看一下我添加到我的答案中的最新版本,最新版本的node.js实际上有一个promise版本的fs接口其中也可以使用。 @ant0nio - promisify() 不会使它们同步,它们仍然是异步的,但会返回承诺。由于使用了asyncawait,代码可能看起来更加同步,它们适用于任何返回承诺的异步函数。 我不得不说,如果你能举例说明如何以手动方式添加 Promise 包装器,这对我来说非常有帮助,可以完全理解这个主题。如果它不打扰你,我(也许还有其他一些 nodejs 新手)会很感激。谢谢!

以上是关于异步和递归目录扫描,用于 Nodejs 和 Expressjs 中的文件列表的主要内容,如果未能解决你的问题,请参考以下文章

nodejs异步到同步

nodejs异步到同步

基于递归 Promise 的目录读取

Nodejs异步递归文件夹大小

如何执行完整的递归目录和文件扫描?

2018-2019 20165208 网络对抗 Exp6 信息搜集与漏洞扫描