关于使用fs.readdir进行异步目录搜索有什么想法吗?我意识到我们可以引入递归,并调用read目录函数来读取下一个目录,但我有点担心它不是异步的…
什么好主意吗?我已经看了node-walk,它很棒,但它不能像readdir那样只给我数组中的文件。虽然
寻找这样的输出…
['file1.txt', 'file2.txt', 'dir/file3.txt']
关于使用fs.readdir进行异步目录搜索有什么想法吗?我意识到我们可以引入递归,并调用read目录函数来读取下一个目录,但我有点担心它不是异步的…
什么好主意吗?我已经看了node-walk,它很棒,但它不能像readdir那样只给我数组中的文件。虽然
寻找这样的输出…
['file1.txt', 'file2.txt', 'dir/file3.txt']
当前回答
TypeScript中基于承诺的递归解决方案,使用Array.flat()处理嵌套返回。
import { resolve } from 'path'
import { Dirent } from 'fs'
import * as fs from 'fs'
function getFiles(root: string): Promise<string[]> {
return fs.promises
.readdir(root, { withFileTypes: true })
.then(dirents => {
const mapToPath = (r: string) => (dirent: Dirent): string => resolve(r, dirent.name)
const directoryPaths = dirents.filter(a => a.isDirectory()).map(mapToPath(root))
const filePaths = dirents.filter(a => a.isFile()).map(mapToPath(root))
return Promise.all<string>([
...directoryPaths.map(a => getFiles(a, include)).flat(),
...filePaths.map(a => Promise.resolve(a))
]).then(a => a.flat())
})
}
其他回答
简单,基于异步承诺
const fs = require('fs/promises');
const getDirRecursive = async (dir) => {
try {
const items = await fs.readdir(dir);
let files = [];
for (const item of items) {
if ((await fs.lstat(`${dir}/${item}`)).isDirectory()) files = [...files, ...(await getDirRecursive(`${dir}/${item}`))];
else files.push({file: item, path: `${dir}/${item}`, parents: dir.split("/")});
}
return files;
} catch (e) {
return e
}
};
用法:await getDirRecursive("./public");
独立承诺实现
在这个例子中,我使用的是when.js承诺库。
var fs = require('fs')
, path = require('path')
, when = require('when')
, nodefn = require('when/node/function');
function walk (directory, includeDir) {
var results = [];
return when.map(nodefn.call(fs.readdir, directory), function(file) {
file = path.join(directory, file);
return nodefn.call(fs.stat, file).then(function(stat) {
if (stat.isFile()) { return results.push(file); }
if (includeDir) { results.push(file + path.sep); }
return walk(file, includeDir).then(function(filesInDir) {
results = results.concat(filesInDir);
});
});
}).then(function() {
return results;
});
};
walk(__dirname).then(function(files) {
console.log(files);
}).otherwise(function(error) {
console.error(error.stack || error);
});
我包含了一个可选参数includeDir,如果设置为true,它将在文件列表中包含目录。
这是另一个实现。上述解决方案都没有任何限制,因此如果您的目录结构很大,它们都会崩溃并最终耗尽资源。
var async = require('async');
var fs = require('fs');
var resolve = require('path').resolve;
var scan = function(path, concurrency, callback) {
var list = [];
var walker = async.queue(function(path, callback) {
fs.stat(path, function(err, stats) {
if (err) {
return callback(err);
} else {
if (stats.isDirectory()) {
fs.readdir(path, function(err, files) {
if (err) {
callback(err);
} else {
for (var i = 0; i < files.length; i++) {
walker.push(resolve(path, files[i]));
}
callback();
}
});
} else {
list.push(path);
callback();
}
}
});
}, concurrency);
walker.push(path);
walker.drain = function() {
callback(list);
}
};
使用50的并发工作得非常好,并且几乎和小型目录结构的简单实现一样快。
TypeScript中基于承诺的递归解决方案,使用Array.flat()处理嵌套返回。
import { resolve } from 'path'
import { Dirent } from 'fs'
import * as fs from 'fs'
function getFiles(root: string): Promise<string[]> {
return fs.promises
.readdir(root, { withFileTypes: true })
.then(dirents => {
const mapToPath = (r: string) => (dirent: Dirent): string => resolve(r, dirent.name)
const directoryPaths = dirents.filter(a => a.isDirectory()).map(mapToPath(root))
const filePaths = dirents.filter(a => a.isFile()).map(mapToPath(root))
return Promise.all<string>([
...directoryPaths.map(a => getFiles(a, include)).flat(),
...filePaths.map(a => Promise.resolve(a))
]).then(a => a.flat())
})
}
用递归
var fs = require('fs')
var path = process.cwd()
var files = []
var getFiles = function(path, files){
fs.readdirSync(path).forEach(function(file){
var subpath = path + '/' + file;
if(fs.lstatSync(subpath).isDirectory()){
getFiles(subpath, files);
} else {
files.push(path + '/' + file);
}
});
}
调用
getFiles(path, files)
console.log(files) // will log all files in directory