在基于 Deno / Node.js 编写程序,时常会遇到类似这样的需求:“列出(遍历)目录下的所有文件,包括子目录“。如何实现这一点呢,很显然可以使用一些已写好的工具库,如 node-rdnode-walkglob 等;但她们或多或少,都有些弊端,不方便使用;那么手动写一个方法,来实现“同步获取指定目录下所有文件“呢?本篇文章,即同大家一起探讨下这个问题。

Deno 如何同步获取指定目录下所有文件?

本文首发于个人最新博客Node.js 如何同步获取指定目录下所有文件? | 静轩之别苑

如果要获得指定目录下文件,您可以使用 fs.readdirfs.readdirSync 方法。fs 包含在 Node.js 核心中,因此无需安装任何软件。这非常简单,具体代码如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
const fs = require('fs');
const exampleFolder = './examples/';
// 异步调用
fs.readdir(exampleFolder, (err, files) => {
files.forEach(file => {
console.log(file);
});
});
// 同步调用
fs.readdirSync(exampleFolder).forEach(file => {
console.log(file);
});

但指定的目录下,除了文件,也可能存在文件夹,这就要求需要通过递归循环遍历:如果判定为文件夹,则继续往文件夹内部走,直到该目录只有文件,而没有文件夹才停止。只是一个目录,同步获取其内容,这很容易;但多重嵌套的文件夹(目录),因为不知道何时能遍历完毕,想同步拿到结果,传统的递归遍历,是不足以完成。这就需要借助已经使用非常高频的 async/await 函数。

下面是基于 Deno 以及 Node.js 的两种实现;原理相同,都是基于 TypeScript 编写;只不过调用 API 不同;考虑到以 Deno 语言编写方式,更方便运行,也是未来新趋势,就将其写在前面;您只需安装下 Deno,运行如下命令即可:

Deno 同步获取指定目录下所有文件

1
deno run --unstable --allow-net --allow-read deep-loop-travesal.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* @desc 深度循环遍历,检索指定目录下所有 .js .ts 文件,并 push 到指定数组;
* @param directory - 指定目录
* @param filePathArr - 指定数组
*/
import * as path from "https://deno.land/std/path/mod.ts";
const deepLoopTraversal = async (directory: string, filePathArr: any[]) => {
for await (const dirEntry of Deno.readDir(directory)) {
const filename = dirEntry.name
console.log(filename)
const filePath = path.join(directory, filename);
const stats = Deno.statSync(filePath);
if (stats.isDirectory) {
await deepLoopTraversal(filePath, filePathArr)
} else {
const isFile = stats.isFile;
const extname = isFile ? path.extname(filePath) : '';
if (extname === '.js' || extname === '.ts') {
filePathArr.push(filePath)
}
}
}
}
const main = async () => {
const filePathArr: Array<string> = []
await deepLoopTraversal(Deno.cwd(), filePathArr)
console.log(`您指定的目录下,是 .js 或 .ts 文件,有以下内容:`)
console.log(filePathArr)
}
main()

这里有必要申明的是,此处的 for 循环遍历,是不能够使用 forEach 等方法的;这具体缘由,看下 forEach 的方法说明,就了然了:它接收第一个参数,是 callback,即便对它进行 async,但在外层,却是不知何里面是何时遍历完成,这就导致了不同步,从而引起问题。如果您想使用,可以看 StackoverFlow 上大家给出的一些办法:Using async/await with a forEach loop

Node.js 同步获取指定目录下所有文件

以下为 Node.js 写法,启动用到了 fs-extra npm 依赖,需要手动安装下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @desc 深度循环遍历,检索指定目录下所有 .js .ts 文件,并 push 到指定数组;
* @param directory - 指定目录
* @param filePathArr - 指定数组
*/
import * as path from 'path';
const fse = require('fs-extra');
const deepLoopTraversal = async (directory: string, filePathArr: any[]) => {
const promiseList: Promise<any>[] = [];
const filesList = await fse.readdir(directory);
for (let i = 0, len = filesList.length; i < len; i++) {
const filename = filesList[i];
const filePath = path.join(directory, filename);
const stats = await fse.stat(filePath);
if (stats.isDirectory()) {
await deepLoopTraversal(filePath, filePathArr);
} else {
const isFile = stats.isFile();
const extname = isFile ? path.extname(filePath) : '';
if (extname === '.js' || extname === '.ts') {
filePathArr.push(filePath)
}
}
}
}
const main = async () => {
const filePathArr: Array<string> = []
await deepLoopTraversal(process.cwd(), filePathArr)
console.log(`您指定的目录下,是 .js 或 .ts 文件,有以下内容:`)
console.log(filePathArr)
}
main()

实例说明

建造「测试目录」如下,对其进行运行如上脚本,将会得到什么结果呢?

1
2
3
4
5
6
7
.
├── a-dir
│   ├── c-file.js
│   └── d-dir
│   └── e-file.js
├── b-file.js
└── deep-loop-travesal.ts

运行脚本,将得到如下结果(因为目录首字母、与文件首字母,决定了 readDir 得到的数组内容顺序,从而使得有以下结果):

1
2
3
4
5
6
[
./~yun/a-dir/c-file.js",
./~yun/a-dir/d-dir/e-file.js",
./~yun/b-file.js",
./~yun/deep-loop-travesal.ts"
]

问题思考

这里有个问题,需要思考下;上面所得到文件数组,顺序由每层文件名、文件夹名所决定。如果想要以从外至内的顺序,得到最终的结果数组,那么该如何处理呢?Promise.all 是解决这个问题的一剂良方。如果判定是文件夹,则将对它的递归循环,放置于一个数组(promiseList),后面将该数组传递给 Promise.all 方法予以调用,问题迎刃而解(目录中内容,将会延迟遍历)。

Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个 Promise 实例, 那个输入的所有 promise 的 resolve 回调的结果是一个数组。这个 Promise 的 resolve 回调执行是在所有输入的 promise 的 resolve 回调都结束,或者输入的 iterable 里没有 promise 了的时候。它的 reject 回调执行是,只要任何一个输入的 promise 的 reject 回调执行或者输入不合法的 promise 就会立即抛出错误,并且 reject 的是第一个抛出的错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* @file deep-loop-travesal.ts
* @desc 深度循环遍历,检索指定目录下所有 .js .ts 文件,并 push 到指定数组;
* @param directory - 指定目录
* @param filePathArr - 指定数组
*/
import * as path from "https://deno.land/std/path/mod.ts";
const deepLoopTraversal = async (directory: string, filePathArr: any[]) => {
const promiseList: Promise<any>[] = [];
for await (const dirEntry of Deno.readDir(directory)) {
const filename = dirEntry.name
const filePath = path.join(directory, filename);
const stats = Deno.statSync(filePath);
if (stats.isDirectory) {
promiseList.push(deepLoopTraversal(filePath, filePathArr));
} else {
const isFile = stats.isFile;
const extname = isFile ? path.extname(filePath) : '';
if (extname === '.js' || extname === '.ts') {
filePathArr.push(filePath)
}
}
}
if (promiseList.length) {
await Promise.all(promiseList);
}
}
const main = async () => {
const filePathArr: Array<string> = []
await deepLoopTraversal(Deno.cwd(), filePathArr)
console.log(`您指定的目录下,是 .js 或 .ts 文件,有以下内容:`)
console.log(filePathArr)
}
main()

依旧是上面的「测试目录」,运行如上改造后的脚本,将得到如下结果,结果符合预期;

1
2
3
4
5
6
[
~/yun/b-file.js",
~/yun/deep-loop-travesal.ts",
~/yun/a-dir/c-file.js",
~/yun/a-dir/d-dir/e-file.js"
]

以上,便是关于“Node.js/Deno,如何同步获取指定目录下所有文件“问题的一点,如有谬误,欢请斧正。


倾城之链作为一个开放平台,旨在云集全球优秀网站,探索互联网中更广阔的世界;在这里,你可以轻松发现、学习、分享更多有用或有趣的事物。

小程序码 - 倾城之链

您可能感兴趣的文章


静晴轩 ~ 晚晴幽草轩
个人微信公众号晚晴幽草轩;名字取自:“天意怜幽草,人间重晚晴”。
专注互联网开发(Web 大前端、快应用、小程序),以及分享优质网站、AI 应用、效率工具、心得感悟等内容。

文章目录
  1. 1. Deno 同步获取指定目录下所有文件
  2. 2. Node.js 同步获取指定目录下所有文件
  3. 3. 实例说明
  4. 4. 问题思考
  5. 5. 您可能感兴趣的文章