异步和递归目录扫描,用于 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()
不会使它们同步,它们仍然是异步的,但会返回承诺。由于使用了async
和await
,代码可能看起来更加同步,它们适用于任何返回承诺的异步函数。
我不得不说,如果你能举例说明如何以手动方式添加 Promise 包装器,这对我来说非常有帮助,可以完全理解这个主题。如果它不打扰你,我(也许还有其他一些 nodejs 新手)会很感激。谢谢!以上是关于异步和递归目录扫描,用于 Nodejs 和 Expressjs 中的文件列表的主要内容,如果未能解决你的问题,请参考以下文章