跳到主要内容

文件系统操作

文件系统概述

文件系统是操作系统用来管理和存储文件的一种机制。在Node.js中,我们可以通过fs模块来访问和操作文件系统。fs模块提供了丰富的API,允许我们进行文件的创建、读取、写入、删除等操作。

fs模块的引入

使用fs模块之前,需要先引入它:

const fs = require('fs');

同步和异步操作

Node.js的fs模块提供了同步和异步两种操作方式:

  • 同步操作:阻塞代码执行,直到操作完成
  • 异步操作:不阻塞代码执行,通过回调函数处理结果

注意:在实际应用中,推荐使用异步操作,以避免阻塞主线程。只有在某些特殊情况下,如脚本初始化时,才考虑使用同步操作。

文件读取

异步读取文件

fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('读取文件失败:', err);
return;
}
console.log('文件内容:', data);
});

同步读取文件

try {
const data = fs.readFileSync('example.txt', 'utf8');
console.log('文件内容:', data);
} catch (err) {
console.error('读取文件失败:', err);
}

流式读取文件

对于大文件,可以使用流式读取,以避免一次性将文件加载到内存中。

const readStream = fs.createReadStream('large-file.txt', 'utf8');

readStream.on('data', (chunk) => {
console.log('读取到数据块:', chunk);
});

readStream.on('end', () => {
console.log('文件读取完成');
});

readStream.on('error', (err) => {
console.error('读取文件错误:', err);
});

文件写入

异步写入文件

const content = '这是要写入的内容';

fs.writeFile('example.txt', content, 'utf8', (err) => {
if (err) {
console.error('写入文件失败:', err);
return;
}
console.log('文件写入成功');
});

同步写入文件

try {
const content = '这是要写入的内容';
fs.writeFileSync('example.txt', content, 'utf8');
console.log('文件写入成功');
} catch (err) {
console.error('写入文件失败:', err);
}

追加写入

const content = '这是要追加的内容';

fs.appendFile('example.txt', content, 'utf8', (err) => {
if (err) {
console.error('追加内容失败:', err);
return;
}
console.log('内容追加成功');
});

流式写入文件

const writeStream = fs.createWriteStream('output.txt', 'utf8');

writeStream.write('第一行内容\n');
writeStream.write('第二行内容\n');
writeStream.end('结束内容\n');

writeStream.on('finish', () => {
console.log('文件写入完成');
});

writeStream.on('error', (err) => {
console.error('写入文件错误:', err);
});

文件和目录操作

检查文件是否存在

fs.access('example.txt', fs.constants.F_OK, (err) => {
console.log(err ? '文件不存在' : '文件存在');
});

获取文件信息

fs.stat('example.txt', (err, stats) => {
if (err) {
console.error('获取文件信息失败:', err);
return;
}
console.log('是否为文件:', stats.isFile());
console.log('是否为目录:', stats.isDirectory());
console.log('文件大小(字节):', stats.size);
console.log('创建时间:', stats.birthtime);
console.log('修改时间:', stats.mtime);
});

创建目录

fs.mkdir('new-directory', (err) => {
if (err) {
console.error('创建目录失败:', err);
return;
}
console.log('目录创建成功');
});

// 创建多级目录
fs.mkdir('parent/child/grandchild', { recursive: true }, (err) => {
if (err) {
console.error('创建多级目录失败:', err);
return;
}
console.log('多级目录创建成功');
});

读取目录内容

fs.readdir('.', (err, files) => {
if (err) {
console.error('读取目录失败:', err);
return;
}
console.log('目录内容:', files);
});

删除文件

fs.unlink('example.txt', (err) => {
if (err) {
console.error('删除文件失败:', err);
return;
}
console.log('文件删除成功');
});

删除目录

fs.rmdir('empty-directory', (err) => {
if (err) {
console.error('删除目录失败:', err);
return;
}
console.log('目录删除成功');
});

// 删除非空目录
fs.rmdir('non-empty-directory', { recursive: true }, (err) => {
if (err) {
console.error('删除非空目录失败:', err);
return;
}
console.log('非空目录删除成功');
});

重命名文件或目录

fs.rename('old-name.txt', 'new-name.txt', (err) => {
if (err) {
console.error('重命名失败:', err);
return;
}
console.log('文件重命名成功');
});

文件权限

在Unix/Linux系统中,文件权限是一个重要的概念。fs模块提供了修改文件权限的方法。

// 设置文件权限,0o644表示所有者可读写,组用户和其他用户可读
fs.chmod('example.txt', 0o644, (err) => {
if (err) {
console.error('修改文件权限失败:', err);
return;
}
console.log('文件权限修改成功');
});

路径处理

在进行文件系统操作时,通常需要处理文件路径。Node.js提供了path模块来帮助我们处理路径。

const path = require('path');

// 获取文件扩展名
const ext = path.extname('example.txt'); // 输出: '.txt'

// 获取文件名(不含扩展名)
const baseName = path.basename('example.txt', path.extname('example.txt')); // 输出: 'example'

// 连接路径
const fullPath = path.join(__dirname, 'folder', 'example.txt');

// 将相对路径转换为绝对路径
const absolutePath = path.resolve('folder', 'example.txt');

实用示例

复制文件

const fs = require('fs');
const path = require('path');

function copyFile(sourcePath, targetPath) {
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(sourcePath);
const writeStream = fs.createWriteStream(targetPath);

readStream.on('error', reject);
writeStream.on('error', reject);
writeStream.on('finish', resolve);

readStream.pipe(writeStream);
});
}

// 使用示例
copyFile('source.txt', 'destination.txt')
.then(() => console.log('文件复制成功'))
.catch(err => console.error('文件复制失败:', err));

读取目录下所有文件

const fs = require('fs');
const path = require('path');

function readAllFiles(dir) {
const files = fs.readdirSync(dir);
const results = [];

files.forEach(file => {
const filePath = path.join(dir, file);
const stats = fs.statSync(filePath);

if (stats.isFile()) {
results.push(filePath);
} else if (stats.isDirectory()) {
results.push(...readAllFiles(filePath));
}
});

return results;
}

// 使用示例
const allFiles = readAllFiles('.');
console.log('所有文件:', allFiles);

最佳实践

  • 优先使用异步API,避免阻塞主线程
  • 始终处理回调函数中的错误
  • 使用流式API处理大文件
  • 使用path模块处理文件路径,确保跨平台兼容性
  • 注意文件权限问题,特别是在Unix/Linux系统上
  • 在操作文件前,检查文件或目录是否存在
  • 对于复杂的文件操作,可以考虑使用第三方库,如fs-extra,它提供了更多便捷的方法