您现在的位置是:网站首页 > 目录操作文章详情
目录操作
陈川
【
Node.js
】
41036人已围观
6725字
Node.js 提供了丰富的文件系统操作能力,其中目录操作是核心功能之一。通过 fs
模块,开发者可以轻松实现目录的创建、删除、遍历等操作,同时结合异步和同步方法满足不同场景需求。
目录的创建
在 Node.js 中,创建目录主要通过 fs.mkdir
和 fs.mkdirSync
实现。异步方法 fs.mkdir
接受回调函数,而同步方法 fs.mkdirSync
会阻塞代码执行直到操作完成。
const fs = require('fs');
// 异步创建目录
fs.mkdir('./newDir', (err) => {
if (err) throw err;
console.log('目录创建成功');
});
// 同步创建目录
try {
fs.mkdirSync('./newDirSync');
console.log('目录创建成功');
} catch (err) {
console.error(err);
}
如果需要创建多级目录(即递归创建),可以传递 { recursive: true }
选项:
// 异步递归创建目录
fs.mkdir('./parent/child/grandchild', { recursive: true }, (err) => {
if (err) throw err;
console.log('多级目录创建成功');
});
目录的删除
删除目录使用 fs.rmdir
或 fs.rmdirSync
。注意:目录必须为空才能被删除,否则会抛出错误。
// 异步删除空目录
fs.rmdir('./emptyDir', (err) => {
if (err) throw err;
console.log('目录删除成功');
});
// 同步删除空目录
try {
fs.rmdirSync('./emptyDirSync');
console.log('目录删除成功');
} catch (err) {
console.error(err);
}
对于非空目录,可以使用 fs.rm
或第三方库如 rimraf
:
// 使用 fs.rm 删除非空目录(Node.js 14.14.0+)
fs.rm('./nonEmptyDir', { recursive: true, force: true }, (err) => {
if (err) throw err;
console.log('非空目录删除成功');
});
目录的读取
读取目录内容使用 fs.readdir
或 fs.readdirSync
,返回目录中的文件和子目录名称数组。
// 异步读取目录
fs.readdir('./targetDir', (err, files) => {
if (err) throw err;
console.log('目录内容:', files); // 输出: ['file1.txt', 'subdir', 'app.js']
});
// 同步读取目录
try {
const files = fs.readdirSync('./targetDir');
console.log('目录内容:', files);
} catch (err) {
console.error(err);
}
可以通过 withFileTypes: true
选项获取更详细的信息:
fs.readdir('./targetDir', { withFileTypes: true }, (err, entries) => {
if (err) throw err;
entries.forEach(entry => {
console.log(
`${entry.name} - ${entry.isDirectory() ? '目录' : '文件'}`
);
});
});
目录的遍历
递归遍历目录是常见需求,以下示例展示如何实现深度优先遍历:
const path = require('path');
function traverseDir(dirPath) {
const files = fs.readdirSync(dirPath);
files.forEach(file => {
const fullPath = path.join(dirPath, file);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
console.log(`进入目录: ${fullPath}`);
traverseDir(fullPath); // 递归调用
} else {
console.log(`文件: ${fullPath}`);
}
});
}
traverseDir('./project');
对于异步遍历,可以使用 fs.promises
API:
async function asyncTraverseDir(dirPath) {
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
await asyncTraverseDir(fullPath);
} else {
console.log(`文件: ${fullPath}`);
}
}
}
asyncTraverseDir('./project').catch(console.error);
目录的监控
使用 fs.watch
可以监控目录变化,但需要注意不同平台的差异性:
const watcher = fs.watch('./watchDir', (eventType, filename) => {
console.log(`事件类型: ${eventType}, 文件: ${filename}`);
});
// 10秒后关闭监控
setTimeout(() => {
watcher.close();
console.log('停止目录监控');
}, 10000);
更稳定的方案是使用 chokidar
第三方库:
const chokidar = require('chokidar');
const watcher = chokidar.watch('./watchDir', {
ignored: /(^|[\/\\])\../, // 忽略隐藏文件
persistent: true
});
watcher
.on('add', path => console.log(`文件添加: ${path}`))
.on('change', path => console.log(`文件修改: ${path}`))
.on('unlink', path => console.log(`文件删除: ${path}`));
目录路径操作
Node.js 的 path
模块专门处理路径相关操作,以下是一些常见用法:
const path = require('path');
// 路径拼接
const fullPath = path.join(__dirname, 'files', 'data.json');
console.log(fullPath); // 输出完整路径
// 获取路径的目录名
console.log(path.dirname(fullPath)); // 输出目录部分
// 获取路径的文件名
console.log(path.basename(fullPath)); // 输出: data.json
console.log(path.basename(fullPath, '.json')); // 输出: data
// 获取扩展名
console.log(path.extname(fullPath)); // 输出: .json
// 路径解析
console.log(path.parse(fullPath));
/* 输出:
{
root: '/',
dir: '/home/user/files',
base: 'data.json',
ext: '.json',
name: 'data'
}
*/
临时目录处理
处理临时目录时,可以使用 os.tmpdir()
获取系统临时目录路径:
const os = require('os');
const tempDir = os.tmpdir();
console.log(`系统临时目录: ${tempDir}`);
// 创建临时目录示例
const tempDirPath = path.join(tempDir, `myapp-${Date.now()}`);
fs.mkdirSync(tempDirPath);
console.log(`临时目录创建于: ${tempDirPath}`);
// 使用后清理
process.on('exit', () => {
fs.rmSync(tempDirPath, { recursive: true });
});
目录权限检查
检查目录是否存在及其权限可以使用 fs.access
或 fs.accessSync
:
// 异步检查目录是否存在并可写
fs.access('./targetDir', fs.constants.F_OK | fs.constants.W_OK, (err) => {
if (err) {
console.error('目录不存在或不可写');
} else {
console.log('目录存在且可写');
}
});
// 同步版本
try {
fs.accessSync('./targetDir', fs.constants.R_OK);
console.log('目录可读');
} catch (err) {
console.error('目录不可读');
}
目录操作的实际应用
一个常见的应用场景是项目初始化时创建目录结构:
const projectStructure = [
'src/',
'src/components/',
'src/styles/',
'public/',
'public/images/',
'config/'
];
function initProject(basePath) {
projectStructure.forEach(dir => {
const dirPath = path.join(basePath, dir);
try {
fs.mkdirSync(dirPath, { recursive: true });
console.log(`创建目录: ${dirPath}`);
} catch (err) {
console.error(`创建目录失败: ${dirPath}`, err);
}
});
}
initProject('./my-project');
另一个场景是清理构建产物:
function cleanBuild() {
const buildDirs = ['dist/', 'build/', '.cache/'];
buildDirs.forEach(dir => {
if (fs.existsSync(dir)) {
fs.rmSync(dir, { recursive: true, force: true });
console.log(`已删除: ${dir}`);
}
});
}
cleanBuild();
目录操作的性能考虑
处理大量文件时,同步操作会阻塞事件循环,影响性能。此时应该:
- 使用异步方法
- 限制并发操作数量
- 考虑使用工作线程
以下示例展示如何使用队列控制并发:
const { EventEmitter } = require('events');
class DirProcessor extends EventEmitter {
constructor(concurrency = 4) {
super();
this.concurrency = concurrency;
this.queue = [];
this.processing = 0;
}
addTask(task) {
this.queue.push(task);
this.process();
}
process() {
while (this.processing < this.concurrency && this.queue.length) {
const task = this.queue.shift();
this.processing++;
task(() => {
this.processing--;
this.process();
if (this.processing === 0 && this.queue.length === 0) {
this.emit('done');
}
});
}
}
}
// 使用示例
const processor = new DirProcessor(2);
const dirs = ['dir1', 'dir2', 'dir3', 'dir4', 'dir5'];
dirs.forEach(dir => {
processor.addTask(done => {
fs.readdir(dir, (err, files) => {
if (err) return console.error(err);
console.log(`${dir} 包含 ${files.length} 个文件`);
done();
});
});
});
processor.on('done', () => console.log('所有目录处理完成'));
上一篇: 文件监视功能
下一篇: 路径处理模块(path)