Node.js 文件系统 (File System)
Node.js - 文件系统 (fs 模块)
Section titled “Node.js - 文件系统 (fs 模块)”Node.js 提供了一个内置模块,称为 fs(文件系统),用于与操作系统上的文件系统进行交互。它允许您读取、写入、更新、删除和管理文件和目录。
可以使用 require 导入 fs 模块:
const fs = require('fs');或者,对于基于 Promise 的操作(推荐用于现代异步代码):
const fsPromises = require('fs').promises;// 或者使用解构赋值:const { readFile, writeFile } = require('fs').promises;同步(Synchronous) vs. 异步(Asynchronous)操作
Section titled “同步(Synchronous) vs. 异步(Asynchronous)操作”fs 模块中的大多数方法都有同步和异步两种形式。
- 异步方法(例如,
fs.readFile()、fs.writeFile()):这些方法在后台执行操作,不会阻塞 Node.js 事件循环(event loop)。它们通常接受一个回调函数(callback function)作为最后一个参数,该函数在操作完成(或出错)时执行。回调函数的第一个参数通常是一个错误对象(如果成功则为null)。这是 Node.js 服务器中进行 I/O 操作的首选方法,以保持并发性和响应能力。 - 同步方法(例如,
fs.readFileSync()、fs.writeFileSync()):这些方法会阻塞整个 Node.js 进程,直到文件操作完成。虽然它们对于脚本或命令行工具来说更简单,但在处理并发请求的 Web 服务器或应用程序中应避免使用同步方法,因为它们会暂停执行并严重影响性能。 - 基于 Promise 的方法(
fs.promises):这些方法使用 JavaScript Promise 提供了一种替代的异步接口,通常与async/await语法一起使用,相比嵌套回调可以写出更清晰的异步代码。示例:await fsPromises.readFile(...)。
示例:读取文件(异步 vs. 同步)
Section titled “示例:读取文件(异步 vs. 同步)”首先,创建一个名为 input.txt 的文本文件,内容如下:
Node.js FundamentalsLearning about the file system module.现在,创建一个名为 main.js 的 JavaScript 文件:
const fs = require('fs');
// 使用回调进行异步读取console.log('Starting asynchronous read...');fs.readFile('input.txt', 'utf8', (err, data) => { if (err) { console.error('Error reading file asynchronously:', err); return; } console.log('Asynchronous Read Content:\n' + data);});console.log('... Asynchronous read initiated, continuing execution ...');
// 同步读取console.log('\nStarting synchronous read...');try { const dataSync = fs.readFileSync('input.txt', 'utf8'); console.log('Synchronous Read Content:\n' + dataSync);} catch (err) { console.error('Error reading file synchronously:', err);}console.log('... Synchronous read finished ...');
console.log('\nProgram Ended');运行脚本:
$ node main.js预期输出(异步部分的顺序可能略有不同):
Starting asynchronous read...... Asynchronous read initiated, continuing execution ...
Starting synchronous read...Synchronous Read Content:Node.js FundamentalsLearning about the file system module.... Synchronous read finished ...
Program EndedAsynchronous Read Content:Node.js FundamentalsLearning about the file system module.请注意,‘continuing execution’ 和同步读取日志出现在异步读取内容之前。这表明了异步操作的非阻塞特性。
常见文件系统操作
Section titled “常见文件系统操作”让我们使用带有回调的异步示例来探索一些关键的 fs 方法。请记住,在现代代码中通常首选基于 Promise 的版本(fs.promises)。
打开文件 (fs.open)
Section titled “打开文件 (fs.open)”语法:fs.open(path, flags[, mode], callback)
path:文件路径。flags:指定如何打开文件(例如,‘r’ 表示读取,‘w’ 表示写入,‘a’ 表示追加)。请参阅下表。mode(可选):如果创建文件,设置文件权限(默认值:0o666)。callback:调用时带参数(err, fd)的函数,其中fd是文件描述符(代表已打开文件的数字)。
常用标志(Flags):
Section titled “常用标志(Flags):”| 标志 (Flag) | 描述 |
|---|---|
'r' | 以读取模式打开文件。如果文件不存在,会发生错误。 |
'r+' | 以读写模式打开文件。如果文件不存在,会发生错误。 |
'w' | 以写入模式打开文件。如果文件不存在则创建,如果存在则截断(清空内容)。 |
'w+' | 以读写模式打开文件。如果文件不存在则创建,如果存在则截断。 |
'a' | 以追加模式打开文件。如果文件不存在则创建。 |
'a+' | 以读写和追加模式打开文件。如果文件不存在则创建。 |
示例:
const fs = require('fs');
console.log('Going to open input.txt for reading...');fs.open('input.txt', 'r', (err, fd) => { if (err) { return console.error('Error opening file:', err); } console.log('File opened successfully! File descriptor:', fd);
// 完成后记得关闭文件描述符 fs.close(fd, (closeErr) => { if (closeErr) { console.error('Error closing file:', closeErr); } console.log('File closed successfully.'); });});注意:使用 fs.readFile 或 fs.writeFile 等更高级别的函数通常更简单,因为它们会自动处理文件的打开和关闭。
获取文件信息 (fs.stat)
Section titled “获取文件信息 (fs.stat)”语法:fs.stat(path, callback)
path:文件或目录的路径。callback:调用时带参数(err, stats)的函数,其中stats是包含文件详细信息的fs.Stats对象。
stats 对象包含有用的属性(例如 size、mtime)和方法:
| 方法 | 描述 |
|---|---|
stats.isFile() | 如果是常规文件,返回 true。 |
stats.isDirectory() | 如果是目录,返回 true。 |
stats.isSymbolicLink() | 如果是符号链接,返回 true。 |
示例:
const fs = require('fs');
console.log('Getting information for input.txt...');fs.stat('input.txt', (err, stats) => { if (err) { return console.error('Error getting file stats:', err); } console.log('File Stats:', stats); console.log('\nIs File?', stats.isFile()); console.log('Is Directory?', stats.isDirectory()); console.log('Size:', stats.size, 'bytes'); console.log('Last Modified:', stats.mtime);});写入文件 (fs.writeFile)
Section titled “写入文件 (fs.writeFile)”语法:fs.writeFile(file, data[, options], callback)
file:文件路径。data:要写入的数据(String、Buffer、TypedArray、DataView)。options(可选):可以是编码字符串(默认 ‘utf8’)或一个对象{ encoding, mode, flag }(默认标志 ‘w’)。callback:完成时调用,带参数(err)的函数。
如果文件存在,此方法会覆盖文件。使用标志 'a'(或 fs.appendFile)进行追加。
示例:
const fs = require('fs');const content = 'Writing some data using fs.writeFile!';
console.log('Going to write to output.txt');fs.writeFile('output.txt', content, 'utf8', (err) => { if (err) { return console.error('Error writing file:', err); } console.log('Data written successfully!');
// 通过回读来验证 fs.readFile('output.txt', 'utf8', (readErr, data) => { if (readErr) { return console.error('Error reading back file:', readErr); } console.log('Read back content: ' + data); });});读取文件 (fs.readFile)
Section titled “读取文件 (fs.readFile)”语法:fs.readFile(path[, options], callback)
path:文件路径。options(可选):可以是编码字符串(例如,‘utf8’)或一个对象{ encoding, flag }。如果提供了编码,回调中的data将是一个字符串;否则,它将是一个 Buffer。callback:调用时带参数(err, data)的函数。
示例(重温之前的异步示例):
const fs = require('fs');
console.log('Going to read input.txt');fs.readFile('input.txt', 'utf8', (err, data) => { if (err) { return console.error('Error reading file:', err); } console.log('File content:\n' + data);});使用 Buffer
Section titled “使用 Buffer”如果您省略编码,readFile 将返回一个 Buffer。Buffer 是 Node.js 处理二进制数据的方式。
const fs = require('fs');
fs.readFile('input.txt', (err, dataBuffer) => { // 未指定编码 if (err) { return console.error(err); } console.log('Data as Buffer:', dataBuffer); console.log('Data as String:', dataBuffer.toString('utf8'));
// 直接创建 Buffer(现代方式) const bufFromArray = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]); // 从字节数组创建 console.log(bufFromArray.toString()); // 输出: hello
const bufFromString = Buffer.from('world', 'utf8'); console.log(bufFromString);});注意:旧的 new Buffer() 构造函数由于安全问题已被弃用。请改用 Buffer.from()、Buffer.alloc() 或 Buffer.allocUnsafe()。
删除文件 (fs.unlink)
Section titled “删除文件 (fs.unlink)”语法:fs.unlink(path, callback)
path:要删除文件的路径。callback:调用时带参数(err)的函数。
示例(请谨慎使用!):
const fs = require('fs');
// 首先,创建一个要删除的文件fs.writeFile('temp_to_delete.txt', 'Delete me', (err) => { if (err) return console.error('Error creating temp file:', err); console.log('Temporary file created.');
// 现在删除它 console.log('Going to delete temp_to_delete.txt'); fs.unlink('temp_to_delete.txt', (unlinkErr) => { if (unlinkErr) { return console.error('Error deleting file:', unlinkErr); } console.log('File deleted successfully!'); });});创建目录 (fs.mkdir)
Section titled “创建目录 (fs.mkdir)”语法:fs.mkdir(path[, options], callback)
path:要创建目录的路径。options(可选):可以是模式(权限,默认 0o777)或一个对象{ recursive, mode }。recursive: true允许在需要时创建父目录(类似于mkdir -p)。callback:调用时带参数(err)的函数。
示例:
const fs = require('fs');const path = require('path'); // 使用 path 模块以提高跨平台兼容性
const dirPath = path.join('.', 'my_new_directory', 'nested'); // 创建 './my_new_directory/nested'
console.log(`Going to create directory: ${dirPath}`);// 使用 { recursive: true } 以便在需要时创建父目录fs.mkdir(dirPath, { recursive: true }, (err) => { if (err) { return console.error('Error creating directory:', err); } console.log('Directory created successfully!');});读取目录 (fs.readdir)
Section titled “读取目录 (fs.readdir)”语法:fs.readdir(path[, options], callback)
path:要读取目录的路径。options(可选):编码(默认 ‘utf8’)或一个对象{ encoding, withFileTypes }。withFileTypes: true返回fs.Dirent对象而不是仅文件名。callback:调用时带参数(err, files)的函数,其中files是文件名(字符串)或fs.Dirent对象的数组。
示例:
const fs = require('fs');
console.log('Going to read contents of current directory (.)');fs.readdir('.', { withFileTypes: true }, (err, files) => { if (err) { return console.error('Error reading directory:', err); } console.log('\nDirectory Contents:'); files.forEach(dirent => { const type = dirent.isDirectory() ? 'Dir' : 'File'; console.log(` - ${dirent.name} (${type})`); });});移除目录 (fs.rmdir 或 fs.rm)
Section titled “移除目录 (fs.rmdir 或 fs.rm)”语法(fs.rmdir - 较旧,仅用于空目录):fs.rmdir(path, callback)
语法(fs.rm - 较新,推荐):fs.rm(path[, options], callback)
path:要移除目录的路径。options(用于fs.rm):一个对象{ recursive, force }。recursive: true允许移除非空目录。callback:调用时带参数(err)的函数。
示例(使用 fs.rm):
const fs = require('fs');const path = require('path');
const dirToRemove = path.join('.', 'my_new_directory');
console.log(`Going to remove directory: ${dirToRemove}`);// 使用 { recursive: true, force: true } 移除非空目录// 谨慎使用 force,它会抑制一些错误。fs.rm(dirToRemove, { recursive: true }, (err) => { if (err) { // 检查错误是否是由于目录不存在(这可能是预期情况) if (err.code === 'ENOENT') { console.log('Directory does not exist, nothing to remove.'); } else { return console.error('Error removing directory:', err); } } else { console.log('Directory removed successfully!'); }});fs 模块包含更多用于文件系统交互的函数(例如,重命名、更改权限、监视更改等)。有关完整列表和详细描述,请参阅 Node.js 官方文档:
- Node.js 文件系统 API 文档:https://nodejs.org/api/fs.html