Node.js 文件系统基础
章节介绍
Node.js 的文件系统(fs)模块提供了与文件系统交互的 API,让开发者能够读取、写入、创建、删除文件和目录。文件系统操作是 Node.js 的核心功能之一,广泛应用于日志记录、配置管理、数据处理等场景。本教程将详细介绍 Node.js 文件系统的基础操作。
核心知识点
fs 模块概述
fs 模块是 Node.js 的核心模块之一,提供了同步和异步两种方式的文件系统操作。
// 引入 fs 模块
const fs = require('fs');
const path = require('path');
// fs 模块的主要功能:
// 1. 文件读写
// 2. 目录操作
// 3. 文件信息获取
// 4. 文件监听
// 5. 文件权限管理文件路径处理
在操作文件之前,需要先了解如何正确处理文件路径。
const path = require('path');
// 路径拼接
const filePath = path.join(__dirname, 'files', 'test.txt');
console.log('拼接路径:', filePath);
// 路径解析
const parsed = path.parse(filePath);
console.log('路径解析:', parsed);
// 输出:{
// root: '/',
// dir: '/path/to/files',
// base: 'test.txt',
// ext: '.txt',
// name: 'test'
// }
// 获取目录名
const dirName = path.dirname(filePath);
console.log('目录名:', dirName);
// 获取文件名
const baseName = path.basename(filePath);
console.log('文件名:', baseName);
// 获取扩展名
const extName = path.extname(filePath);
console.log('扩展名:', extName);
// 路径规范化
const normalized = path.normalize('/path/to/../to/./file.txt');
console.log('规范化路径:', normalized);
// 绝对路径
const absolute = path.resolve('file.txt');
console.log('绝对路径:', absolute);
// 相对路径
const relative = path.relative('/path/to/file1.txt', '/path/to/file2.txt');
console.log('相对路径:', relative);
// 判断是否为绝对路径
console.log('是否为绝对路径:', path.isAbsolute(filePath));
// 路径分隔符
console.log('路径分隔符:', path.sep); // Windows: '\', Unix: '/'
// 环境变量分隔符
console.log('环境变量分隔符:', path.delimiter); // Windows: ';', Unix: ':'文件读取
Node.js 提供了多种文件读取方式。
同步读取
const fs = require('fs');
const path = require('path');
// 读取文件(同步)
try {
const filePath = path.join(__dirname, 'test.txt');
const content = fs.readFileSync(filePath, 'utf8');
console.log('文件内容:');
console.log(content);
} catch (error) {
console.error('读取文件失败:', error.message);
}
// 读取文件(Buffer)
try {
const filePath = path.join(__dirname, 'test.txt');
const buffer = fs.readFileSync(filePath);
console.log('Buffer 长度:', buffer.length);
console.log('文件内容:', buffer.toString('utf8'));
} catch (error) {
console.error('读取文件失败:', error.message);
}异步读取
const fs = require('fs');
const path = require('path');
// 读取文件(回调方式)
const filePath = path.join(__dirname, 'test.txt');
fs.readFile(filePath, 'utf8', (error, content) => {
if (error) {
console.error('读取文件失败:', error.message);
return;
}
console.log('文件内容:');
console.log(content);
});
// 读取文件(Promise 方式)
const fsPromises = require('fs').promises;
async function readFileAsync() {
try {
const filePath = path.join(__dirname, 'test.txt');
const content = await fsPromises.readFile(filePath, 'utf8');
console.log('文件内容:');
console.log(content);
} catch (error) {
console.error('读取文件失败:', error.message);
}
}
readFileAsync();文件写入
同步写入
const fs = require('fs');
const path = require('path');
// 写入文件(同步)
try {
const filePath = path.join(__dirname, 'output.txt');
const content = 'Hello, Node.js!';
fs.writeFileSync(filePath, content, 'utf8');
console.log('文件写入成功');
} catch (error) {
console.error('写入文件失败:', error.message);
}
// 追加内容到文件
try {
const filePath = path.join(__dirname, 'output.txt');
const additionalContent = '\n追加的内容';
fs.appendFileSync(filePath, additionalContent, 'utf8');
console.log('内容追加成功');
} catch (error) {
console.error('追加内容失败:', error.message);
}异步写入
const fs = require('fs');
const path = require('path');
// 写入文件(回调方式)
const filePath = path.join(__dirname, 'output.txt');
const content = 'Hello, Node.js!';
fs.writeFile(filePath, content, 'utf8', (error) => {
if (error) {
console.error('写入文件失败:', error.message);
return;
}
console.log('文件写入成功');
});
// 追加内容到文件(回调方式)
fs.appendFile(filePath, '\n追加的内容', 'utf8', (error) => {
if (error) {
console.error('追加内容失败:', error.message);
return;
}
console.log('内容追加成功');
});
// 写入文件(Promise 方式)
const fsPromises = require('fs').promises;
async function writeFileAsync() {
try {
const filePath = path.join(__dirname, 'output.txt');
const content = 'Hello, Node.js!';
await fsPromises.writeFile(filePath, content, 'utf8');
console.log('文件写入成功');
} catch (error) {
console.error('写入文件失败:', error.message);
}
}
writeFileAsync();文件信息获取
const fs = require('fs');
const path = require('path');
// 获取文件信息(同步)
try {
const filePath = path.join(__dirname, 'test.txt');
const stats = fs.statSync(filePath);
console.log('文件信息:');
console.log(' 是否为文件:', stats.isFile());
console.log(' 是否为目录:', stats.isDirectory());
console.log(' 文件大小:', stats.size, '字节');
console.log(' 创建时间:', stats.birthtime);
console.log(' 修改时间:', stats.mtime);
console.log(' 访问时间:', stats.atime);
console.log(' 权限模式:', stats.mode);
console.log(' inode:', stats.ino);
console.log(' 设备 ID:', stats.dev);
} catch (error) {
console.error('获取文件信息失败:', error.message);
}
// 获取文件信息(异步)
const filePath = path.join(__dirname, 'test.txt');
fs.stat(filePath, (error, stats) => {
if (error) {
console.error('获取文件信息失败:', error.message);
return;
}
console.log('文件信息:');
console.log(' 是否为文件:', stats.isFile());
console.log(' 是否为目录:', stats.isDirectory());
console.log(' 文件大小:', stats.size, '字节');
});
// 检查文件是否存在
fs.access(filePath, fs.constants.F_OK, (error) => {
if (error) {
console.log('文件不存在');
} else {
console.log('文件存在');
}
});
// 检查文件是否可读
fs.access(filePath, fs.constants.R_OK, (error) => {
if (error) {
console.log('文件不可读');
} else {
console.log('文件可读');
}
});
// 检查文件是否可写
fs.access(filePath, fs.constants.W_OK, (error) => {
if (error) {
console.log('文件不可写');
} else {
console.log('文件可写');
}
});目录操作
const fs = require('fs');
const path = require('path');
// 创建目录
try {
const dirPath = path.join(__dirname, 'new-directory');
fs.mkdirSync(dirPath);
console.log('目录创建成功');
} catch (error) {
console.error('创建目录失败:', error.message);
}
// 创建多级目录
try {
const dirPath = path.join(__dirname, 'parent', 'child', 'grandchild');
fs.mkdirSync(dirPath, { recursive: true });
console.log('多级目录创建成功');
} catch (error) {
console.error('创建目录失败:', error.message);
}
// 读取目录
try {
const dirPath = __dirname;
const files = fs.readdirSync(dirPath);
console.log('目录内容:');
files.forEach(file => {
console.log(' ', file);
});
} catch (error) {
console.error('读取目录失败:', error.message);
}
// 读取目录(带详细信息)
try {
const dirPath = __dirname;
const files = fs.readdirSync(dirPath, { withFileTypes: true });
console.log('目录内容(详细信息):');
files.forEach(file => {
const type = file.isDirectory() ? '目录' : '文件';
console.log(` ${file.name} (${type})`);
});
} catch (error) {
console.error('读取目录失败:', error.message);
}
// 删除目录
try {
const dirPath = path.join(__dirname, 'new-directory');
fs.rmdirSync(dirPath);
console.log('目录删除成功');
} catch (error) {
console.error('删除目录失败:', error.message);
}
// 删除非空目录(需要递归删除)
function deleteDirectory(dirPath) {
if (fs.existsSync(dirPath)) {
const files = fs.readdirSync(dirPath);
files.forEach(file => {
const filePath = path.join(dirPath, file);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
deleteDirectory(filePath);
} else {
fs.unlinkSync(filePath);
}
});
fs.rmdirSync(dirPath);
}
}
try {
const dirPath = path.join(__dirname, 'parent');
deleteDirectory(dirPath);
console.log('目录及其内容删除成功');
} catch (error) {
console.error('删除目录失败:', error.message);
}文件删除和重命名
const fs = require('fs');
const path = require('path');
// 删除文件
try {
const filePath = path.join(__dirname, 'output.txt');
fs.unlinkSync(filePath);
console.log('文件删除成功');
} catch (error) {
console.error('删除文件失败:', error.message);
}
// 重命名文件
try {
const oldPath = path.join(__dirname, 'old-name.txt');
const newPath = path.join(__dirname, 'new-name.txt');
fs.renameSync(oldPath, newPath);
console.log('文件重命名成功');
} catch (error) {
console.error('重命名文件失败:', error.message);
}
// 移动文件
try {
const sourcePath = path.join(__dirname, 'file.txt');
const destPath = path.join(__dirname, 'subdirectory', 'file.txt');
fs.renameSync(sourcePath, destPath);
console.log('文件移动成功');
} catch (error) {
console.error('移动文件失败:', error.message);
}实用案例分析
案例 1:文件管理工具
创建一个功能完善的文件管理工具。
// file-manager.js
const fs = require('fs');
const path = require('path');
class FileManager {
constructor(basePath = process.cwd()) {
this.basePath = basePath;
}
// 创建文件
createFile(filename, content = '') {
const filePath = path.join(this.basePath, filename);
try {
fs.writeFileSync(filePath, content, 'utf8');
console.log(`文件 ${filename} 创建成功`);
return true;
} catch (error) {
console.error(`创建文件失败:${error.message}`);
return false;
}
}
// 读取文件
readFile(filename) {
const filePath = path.join(this.basePath, filename);
try {
const content = fs.readFileSync(filePath, 'utf8');
console.log(`文件 ${filename} 的内容:`);
console.log(content);
return content;
} catch (error) {
console.error(`读取文件失败:${error.message}`);
return null;
}
}
// 更新文件
updateFile(filename, content) {
const filePath = path.join(this.basePath, filename);
try {
if (!fs.existsSync(filePath)) {
console.error(`文件 ${filename} 不存在`);
return false;
}
fs.writeFileSync(filePath, content, 'utf8');
console.log(`文件 ${filename} 更新成功`);
return true;
} catch (error) {
console.error(`更新文件失败:${error.message}`);
return false;
}
}
// 删除文件
deleteFile(filename) {
const filePath = path.join(this.basePath, filename);
try {
if (!fs.existsSync(filePath)) {
console.error(`文件 ${filename} 不存在`);
return false;
}
fs.unlinkSync(filePath);
console.log(`文件 ${filename} 删除成功`);
return true;
} catch (error) {
console.error(`删除文件失败:${error.message}`);
return false;
}
}
// 重命名文件
renameFile(oldName, newName) {
const oldPath = path.join(this.basePath, oldName);
const newPath = path.join(this.basePath, newName);
try {
if (!fs.existsSync(oldPath)) {
console.error(`文件 ${oldName} 不存在`);
return false;
}
fs.renameSync(oldPath, newPath);
console.log(`文件 ${oldName} 重命名为 ${newName} 成功`);
return true;
} catch (error) {
console.error(`重命名文件失败:${error.message}`);
return false;
}
}
// 复制文件
copyFile(source, destination) {
const sourcePath = path.join(this.basePath, source);
const destPath = path.join(this.basePath, destination);
try {
if (!fs.existsSync(sourcePath)) {
console.error(`源文件 ${source} 不存在`);
return false;
}
const content = fs.readFileSync(sourcePath);
fs.writeFileSync(destPath, content);
console.log(`文件 ${source} 复制到 ${destination} 成功`);
return true;
} catch (error) {
console.error(`复制文件失败:${error.message}`);
return false;
}
}
// 创建目录
createDirectory(dirname) {
const dirPath = path.join(this.basePath, dirname);
try {
fs.mkdirSync(dirPath, { recursive: true });
console.log(`目录 ${dirname} 创建成功`);
return true;
} catch (error) {
console.error(`创建目录失败:${error.message}`);
return false;
}
}
// 列出目录内容
listDirectory(dirname = '.') {
const dirPath = path.join(this.basePath, dirname);
try {
const files = fs.readdirSync(dirPath, { withFileTypes: true });
console.log(`目录 ${dirname} 的内容:`);
files.forEach(file => {
const type = file.isDirectory() ? '[DIR]' : '[FILE]';
console.log(` ${type} ${file.name}`);
});
return files;
} catch (error) {
console.error(`列出目录失败:${error.message}`);
return [];
}
}
// 获取文件信息
getFileInfo(filename) {
const filePath = path.join(this.basePath, filename);
try {
const stats = fs.statSync(filePath);
const info = {
name: filename,
size: stats.size,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
created: stats.birthtime,
modified: stats.mtime,
accessed: stats.atime
};
console.log('文件信息:');
console.log(` 名称:${info.name}`);
console.log(` 大小:${info.size} 字节`);
console.log(` 类型:${info.isFile ? '文件' : '目录'}`);
console.log(` 创建时间:${info.created}`);
console.log(` 修改时间:${info.modified}`);
return info;
} catch (error) {
console.error(`获取文件信息失败:${error.message}`);
return null;
}
}
// 搜索文件
searchFiles(pattern, dirname = '.') {
const dirPath = path.join(this.basePath, dirname);
const results = [];
function search(currentPath) {
try {
const files = fs.readdirSync(currentPath, { withFileTypes: true });
files.forEach(file => {
const filePath = path.join(currentPath, file.name);
if (file.isDirectory()) {
search(filePath);
} else if (file.name.match(pattern)) {
results.push(filePath);
}
});
} catch (error) {
console.error(`搜索文件失败:${error.message}`);
}
}
search(dirPath);
console.log(`搜索结果(模式:${pattern}):`);
results.forEach(file => {
console.log(` ${file}`);
});
return results;
}
}
// 使用示例
const fileManager = new FileManager();
// 创建文件
fileManager.createFile('test.txt', 'Hello, World!');
// 读取文件
fileManager.readFile('test.txt');
// 更新文件
fileManager.updateFile('test.txt', 'Hello, Node.js!');
// 获取文件信息
fileManager.getFileInfo('test.txt');
// 创建目录
fileManager.createDirectory('subdirectory');
// 复制文件
fileManager.copyFile('test.txt', 'subdirectory/test.txt');
// 列出目录内容
fileManager.listDirectory();
// 搜索文件
fileManager.searchFiles(/\.txt$/);
// 重命名文件
fileManager.renameFile('test.txt', 'renamed.txt');
// 删除文件
fileManager.deleteFile('renamed.txt');案例 2:日志记录器
创建一个功能完善的日志记录器。
// logger.js
const fs = require('fs');
const path = require('path');
class Logger {
constructor(options = {}) {
this.name = options.name || 'App';
this.logDir = options.logDir || path.join(__dirname, 'logs');
this.maxFileSize = options.maxFileSize || 10 * 1024 * 1024; // 10MB
this.maxFiles = options.maxFiles || 5;
this.level = options.level || 'info';
this.levels = {
error: 0,
warn: 1,
info: 2,
debug: 3
};
this.ensureLogDir();
}
ensureLogDir() {
if (!fs.existsSync(this.logDir)) {
fs.mkdirSync(this.logDir, { recursive: true });
}
}
getLogFile() {
const date = new Date();
const filename = `${this.name}-${date.toISOString().split('T')[0]}.log`;
return path.join(this.logDir, filename);
}
formatMessage(level, message) {
const timestamp = new Date().toISOString();
return `[${timestamp}] [${this.name}] [${level.toUpperCase()}] ${message}\n`;
}
shouldLog(level) {
return this.levels[level] <= this.levels[this.level];
}
rotateLogFile(filePath) {
try {
const stats = fs.statSync(filePath);
if (stats.size >= this.maxFileSize) {
const baseName = path.basename(filePath, '.log');
const dirName = path.dirname(filePath);
// 删除最旧的日志文件
const files = fs.readdirSync(dirName)
.filter(file => file.startsWith(baseName) && file.endsWith('.log'))
.sort();
while (files.length >= this.maxFiles) {
const oldestFile = files.shift();
fs.unlinkSync(path.join(dirName, oldestFile));
}
// 重命名当前日志文件
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const newName = `${baseName}-${timestamp}.log`;
fs.renameSync(filePath, path.join(dirName, newName));
}
} catch (error) {
console.error('日志文件轮转失败:', error.message);
}
}
log(level, message) {
if (!this.shouldLog(level)) {
return;
}
const formattedMessage = this.formatMessage(level, message);
const logFile = this.getLogFile();
// 输出到控制台
switch (level) {
case 'error':
console.error(formattedMessage);
break;
case 'warn':
console.warn(formattedMessage);
break;
case 'debug':
console.debug(formattedMessage);
break;
default:
console.log(formattedMessage);
}
// 写入文件
try {
this.rotateLogFile(logFile);
fs.appendFileSync(logFile, formattedMessage, 'utf8');
} catch (error) {
console.error('写入日志文件失败:', error.message);
}
}
error(message) {
this.log('error', message);
}
warn(message) {
this.log('warn', message);
}
info(message) {
this.log('info', message);
}
debug(message) {
this.log('debug', message);
}
}
// 使用示例
const logger = new Logger({
name: 'MyApp',
logDir: path.join(__dirname, 'logs'),
maxFileSize: 1024, // 1KB(用于测试)
maxFiles: 3,
level: 'debug'
});
logger.info('应用启动');
logger.debug('调试信息');
logger.warn('警告信息');
logger.error('错误信息');
// 模拟大量日志
for (let i = 0; i < 100; i++) {
logger.info(`日志消息 ${i}`);
}案例 3:配置文件管理器
创建一个配置文件管理器,支持 JSON 格式的配置文件。
// config-manager.js
const fs = require('fs');
const path = require('path');
class ConfigManager {
constructor(configPath) {
this.configPath = configPath;
this.config = {};
this.loadConfig();
}
// 加载配置文件
loadConfig() {
try {
if (fs.existsSync(this.configPath)) {
const content = fs.readFileSync(this.configPath, 'utf8');
this.config = JSON.parse(content);
console.log('配置文件加载成功');
} else {
console.log('配置文件不存在,使用默认配置');
this.config = {};
}
} catch (error) {
console.error('加载配置文件失败:', error.message);
this.config = {};
}
}
// 保存配置文件
saveConfig() {
try {
const content = JSON.stringify(this.config, null, 2);
fs.writeFileSync(this.configPath, content, 'utf8');
console.log('配置文件保存成功');
return true;
} catch (error) {
console.error('保存配置文件失败:', error.message);
return false;
}
}
// 获取配置值
get(key, defaultValue = undefined) {
const keys = key.split('.');
let value = this.config;
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k];
} else {
return defaultValue;
}
}
return value;
}
// 设置配置值
set(key, value) {
const keys = key.split('.');
let current = this.config;
for (let i = 0; i < keys.length - 1; i++) {
const k = keys[i];
if (!(k in current) || typeof current[k] !== 'object') {
current[k] = {};
}
current = current[k];
}
current[keys[keys.length - 1]] = value;
return this.saveConfig();
}
// 删除配置值
delete(key) {
const keys = key.split('.');
let current = this.config;
for (let i = 0; i < keys.length - 1; i++) {
const k = keys[i];
if (!(k in current) || typeof current[k] !== 'object') {
return false;
}
current = current[k];
}
const lastKey = keys[keys.length - 1];
if (lastKey in current) {
delete current[lastKey];
return this.saveConfig();
}
return false;
}
// 获取所有配置
getAll() {
return { ...this.config };
}
// 合并配置
merge(newConfig) {
this.config = this.deepMerge(this.config, newConfig);
return this.saveConfig();
}
// 深度合并对象
deepMerge(target, source) {
const result = { ...target };
for (const key in source) {
if (source[key] instanceof Object && key in target) {
result[key] = this.deepMerge(target[key], source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
// 重置配置
reset() {
this.config = {};
return this.saveConfig();
}
}
// 使用示例
const configPath = path.join(__dirname, 'config.json');
const configManager = new ConfigManager(configPath);
// 设置配置
configManager.set('app.name', 'My App');
configManager.set('app.version', '1.0.0');
configManager.set('app.port', 3000);
configManager.set('database.host', 'localhost');
configManager.set('database.port', 5432);
configManager.set('database.name', 'myapp');
// 获取配置
console.log('应用名称:', configManager.get('app.name'));
console.log('应用端口:', configManager.get('app.port'));
console.log('数据库主机:', configManager.get('database.host'));
console.log('不存在的配置:', configManager.get('nonexistent', '默认值'));
// 获取所有配置
console.log('所有配置:');
console.log(configManager.getAll());
// 合并配置
configManager.merge({
app: {
debug: true
},
logging: {
level: 'info'
}
});
console.log('合并后的配置:');
console.log(configManager.getAll());
// 删除配置
configManager.delete('app.debug');
console.log('删除后的配置:');
console.log(configManager.getAll());代码示例
示例 1:文件复制工具
// file-copier.js
const fs = require('fs');
const path = require('path');
function copyFile(source, destination, callback) {
const readStream = fs.createReadStream(source);
const writeStream = fs.createWriteStream(destination);
readStream.on('error', (error) => {
console.error('读取文件失败:', error.message);
callback(error);
});
writeStream.on('error', (error) => {
console.error('写入文件失败:', error.message);
callback(error);
});
writeStream.on('finish', () => {
console.log('文件复制成功');
callback(null);
});
readStream.pipe(writeStream);
}
// 使用示例
const sourceFile = path.join(__dirname, 'source.txt');
const destFile = path.join(__dirname, 'destination.txt');
copyFile(sourceFile, destFile, (error) => {
if (error) {
console.error('复制失败');
} else {
console.log('复制完成');
}
});示例 2:目录同步工具
// directory-sync.js
const fs = require('fs');
const path = require('path');
function syncDirectories(source, destination) {
// 确保目标目录存在
if (!fs.existsSync(destination)) {
fs.mkdirSync(destination, { recursive: true });
}
// 读取源目录
const files = fs.readdirSync(source, { withFileTypes: true });
files.forEach(file => {
const sourcePath = path.join(source, file.name);
const destPath = path.join(destination, file.name);
if (file.isDirectory()) {
// 递归同步子目录
syncDirectories(sourcePath, destPath);
} else {
// 复制文件
const sourceStats = fs.statSync(sourcePath);
let shouldCopy = true;
// 检查目标文件是否存在
if (fs.existsSync(destPath)) {
const destStats = fs.statSync(destPath);
// 如果目标文件更新,则不复制
if (destStats.mtime >= sourceStats.mtime) {
shouldCopy = false;
}
}
if (shouldCopy) {
console.log(`复制文件:${file.name}`);
const content = fs.readFileSync(sourcePath);
fs.writeFileSync(destPath, content);
}
}
});
// 删除目标目录中不存在于源目录的文件
const destFiles = fs.readdirSync(destination);
destFiles.forEach(file => {
const sourcePath = path.join(source, file);
const destPath = path.join(destination, file);
if (!fs.existsSync(sourcePath)) {
console.log(`删除文件:${file}`);
fs.unlinkSync(destPath);
}
});
}
// 使用示例
const sourceDir = path.join(__dirname, 'source');
const destDir = path.join(__dirname, 'destination');
syncDirectories(sourceDir, destDir);
console.log('目录同步完成');示例 3:文件监控器
// file-watcher.js
const fs = require('fs');
const path = require('path');
class FileWatcher {
constructor(watchPath) {
this.watchPath = watchPath;
this.watchers = new Map();
}
start() {
console.log(`开始监控:${this.watchPath}`);
const watcher = fs.watch(this.watchPath, (eventType, filename) => {
if (filename) {
const filePath = path.join(this.watchPath, filename);
switch (eventType) {
case 'rename':
if (fs.existsSync(filePath)) {
console.log(`文件创建:${filename}`);
} else {
console.log(`文件删除:${filename}`);
}
break;
case 'change':
console.log(`文件修改:${filename}`);
break;
}
}
});
this.watchers.set(this.watchPath, watcher);
}
stop() {
this.watchers.forEach((watcher, path) => {
watcher.close();
console.log(`停止监控:${path}`);
});
this.watchers.clear();
}
}
// 使用示例
const watchPath = __dirname;
const fileWatcher = new FileWatcher(watchPath);
fileWatcher.start();
// 5 秒后停止监控
setTimeout(() => {
fileWatcher.stop();
}, 5000);实现技巧与注意事项
文件操作最佳实践
- 使用异步操作:避免阻塞事件循环
- 错误处理:始终处理可能的错误
- 路径处理:使用 path 模块处理路径
- 资源清理:确保文件描述符正确关闭
性能优化建议
- 使用流处理大文件:避免一次性读取大文件
- 批量操作:减少文件系统调用次数
- 缓存文件信息:避免重复读取文件信息
- 使用 Promise:简化异步代码
安全注意事项
- 路径验证:防止路径遍历攻击
- 权限检查:确保有足够的权限
- 输入验证:验证用户输入的文件名
- 备份重要文件:避免意外删除
常见问题与解决方案
问题 1:文件路径问题
// 问题代码:硬编码路径
const filePath = 'C:\\Users\\user\\file.txt'; // Windows 特定
// 解决方案:使用 path 模块
const path = require('path');
const filePath = path.join(__dirname, 'file.txt');
// 或使用跨平台路径分隔符
const filePath = 'files' + path.sep + 'file.txt';问题 2:异步操作顺序问题
// 问题代码:异步操作顺序不确定
fs.readFile('file1.txt', (err, data1) => {
console.log('File 1');
});
fs.readFile('file2.txt', (err, data2) => {
console.log('File 2');
});
// 解决方案:使用 Promise 或 async/await
const fsPromises = require('fs').promises;
async function readFiles() {
const data1 = await fsPromises.readFile('file1.txt');
console.log('File 1');
const data2 = await fsPromises.readFile('file2.txt');
console.log('File 2');
}
readFiles();问题 3:文件编码问题
// 问题代码:未指定编码
const content = fs.readFileSync('file.txt'); // 返回 Buffer
// 解决方案:指定编码
const content = fs.readFileSync('file.txt', 'utf8');
// 或使用 Buffer 转换
const buffer = fs.readFileSync('file.txt');
const content = buffer.toString('utf8');问题 4:大文件处理问题
// 问题代码:一次性读取大文件
const content = fs.readFileSync('large-file.txt'); // 可能导致内存溢出
// 解决方案:使用流处理
const readStream = fs.createReadStream('large-file.txt');
const writeStream = fs.createWriteStream('output.txt');
readStream.on('data', (chunk) => {
console.log(`读取了 ${chunk.length} 字节`);
});
readStream.on('end', () => {
console.log('读取完成');
});
readStream.pipe(writeStream);总结
本教程详细介绍了 Node.js 文件系统的基础操作,包括文件读写、目录操作、文件信息获取等重要内容。文件系统操作是 Node.js 编程的核心功能之一,掌握它对于开发各种应用程序至关重要。
通过本集的学习,您应该能够:
- 理解 Node.js 文件系统模块的基本概念
- 使用 fs 模块进行文件读写操作
- 进行目录的创建、读取和删除操作
- 获取和管理文件信息
- 创建实用的文件管理工具
- 避免常见的文件系统操作错误
在下一集中,我们将学习 Node.js 文件系统进阶,包括文件流、异步操作等高级特性。继续加油,您的 Node.js 技能正在不断提升!