【Node.js】01 —— fs模块全解析


🔥【Node.js】 fs模块全解析

📢 引言

在Node.js开发中,fs模块犹如一把万能钥匙,解锁着整个文件系统的操作。从读取文件、写入文件、检查状态到目录管理,无所不能。接下来,我们将逐一揭开fs模块中最常用的那些方法神秘面纱,搭配生动的代码示例,让学习过程变得有趣而高效!🌟


📚 Ⅰ. 同步与异步读取文件

💡 fs.readFileSync() & fs.readFile()

  • fs.readFileSync(path, options) ------同步读取文件。接受两个参数:文件路径回调函数。同步读取文件时,Node.js会阻塞当前执行线程,直到文件读取完毕并将内容返回给调用者。这意味着在文件读取完成之前,程序的其他部分无法继续执行
  • fs.readFile(path, options, callback) ------异步读取文件。接受三个参数:文件路径编码格式回调函数。异步读取文件时,Node.js并不会阻塞主线程,而是将读取操作交由操作系统在后台执行。当文件读取完成后,Node.js会通过回调函数通知用户程序结果
javascript 复制代码
const fs = require('fs');
// 同步读取文件
try {
	const data = fs.readFileSync('./test-read-sync.txt', 'utf8');
	console.log('同步读取文件-File content:', data);
} catch (err) {
	console.error('同步读取文件出错-Error reading file:', err);
}


// 异步读取文件
fs.readFile('./test-read-async.txt', 'utf8', (err, data) => {
	if (err) {
		console.error('异步读取文件出错-Error reading file:', err);
	} else {
		console.log('异步读取文件-File content:', data);
	}
});

虽然同步读取在逻辑上更简单直观,但它的缺点是如果文件很大或者I/O速度较慢,整个程序可能会暂时挂起,影响整体性能。在大部分场景下,尤其是在强调高性能和高并发的应用中,应当优先考虑使用异步I/O。
异步过程中,Node.js可以继续执行其他任务,提高了程序的并发性能。因此,异步读取通常用于避免I/O操作导致的阻塞,特别是在服务器端处理网络请求时,这能够确保多个请求可以并行处理而不受单一文件读取操作的限制。


📃 Ⅱ. 同步与异步写入文件

💡 fs.writeFileSync() & fs.writeFile()

  • fs.writeFileSync(path, data[, options]) ------同步写入文件。接收三个参数:文件路径(必需)、要写入的数据(必需)以及可选的选项对象(如指定字符编码)。如同步读取文件一样,同步写入文件也会阻塞当前执行线程,直到文件完全写入完成。
  • fs.writeFile(path, data[, options], callback) ------异步写入文件。接收三个主要参数:文件路径(必需)、要写入的数据(必需)、可选的选项对象以及一个回调函数。异步写入文件不会阻塞主线程,而是将写入操作安排到事件循环队列中,一旦写入完成,便调用回调函数通知结果。
javascript 复制代码
const fs = require("fs")
// 同步写入文件
try {
	fs.writeFileSync('./test-write-sync.txt', '---这里是被写入的内容---');
	console.log('同步写入文件-File written successfully');
} catch (err) {
	console.error('同步写入文件出错-Error writing file:', err);
}
// 异步写入文件
fs.writeFile('./test-write-async.txt', '---这里是被写入的内容---', (err) => {
	if (err) {
		console.error('异步写入文件出错-Error writing file:', err);
	} else {
		console.log('异步写入文件-File written successfully');
	}
})

🖋 Ⅲ. 同步与异步追加写入文件

💡 fs.appendFileSync() & fs.appendFile()

  • fs.appendFileSync(path, data[, options])------同步追加写入文件。接受两个参数:文件路径要写入的数据
  • fs.appendFile(path, data[, options], callback)------异步追加写入文件。接受三个参数:文件路径要写入的数据以及回调函数
javascript 复制代码
// 导入fs模块,来操作文件
const fs = require("fs")

// 同步追加写入文件
try {
	fs.appendFileSync('./test-append-sync.txt', '---这里是被追加的内容---');
	console.log('同步追加写入文件-File written successfully');
} catch (err) {
	console.error('同步追加写入文件出错-Error writing file:', err);
}

// 异步追加写入文件
fs.appendFile('./test-append-async.txt', '---这里是被追加的内容---', (err) => {
	if (err) {
		console.error('异步追加写入文件出错-Error writing file:', err);
	} else {
		console.log('异步追加写入文件-File written successfully');
	}
})

🚀 Ⅳ. 文件状态检查

💡 fs.stat() & fs.statSync()

  • fs.stat(path, callback) ------异步获取文件状态信息。此方法接收两个参数:文件路径(必需)以及一个回调函数。回调函数接收两个参数:错误对象(如果有错误发生)和一个fs.Stats对象,该对象包含了文件或目录的各种状态信息。
  • fs.statSync(path) ------同步获取文件状态信息。此方法仅接收一个参数:文件路径(必需),并直接返回一个fs.Stats对象,如果不成功,则抛出错误。
javascript 复制代码
const fs = require('fs');

// 异步检查文件状态示例
fs.stat('./test-file.txt', (err, stats) => {
  if (err) {
    console.error('获取文件状态失败:', err);
  } else {
    console.log(`文件类型:${stats.isFile() ? '文件' : stats.isDirectory() ? '目录' : '其他'}`);
    console.log('文件大小(字节):', stats.size);
    console.log('最后一次修改时间:', stats.mtime.toLocaleString());
  }
});
// 同步检查文件状态示例
try {
  const stats = fs.statSync('./test-file.txt');
  console.log(`文件类型:${stats.isFile() ? '文件' : stats.isDirectory() ? '目录' : '其他'}`);
  console.log('文件大小(字节):', stats.size);
  console.log('最后一次修改时间:', stats.mtime.toLocaleString());
} catch (err) {
  console.error('获取文件状态失败:', err);
}

fs.Stats对象包含许多有用的属性,如:

  • .isFile():判断是否为普通文件。
  • .isDirectory():判断是否为目录。
  • .size:文件或目录占用空间的字节数(如果是目录,通常是目录本身的信息所占空间)。
  • .mtime:最后一次修改时间(modification time)的对象,可通过.getTime()获取毫秒时间戳,或.toLocaleString()获取格式化后的本地字符串。

🔥 V. 目录操作

💡 fs.mkdir() & fs.mkdirSync()

  • fs.mkdir(path[, options], callback) ------ 异步创建目录。该方法接收一个必填参数路径,用于指定要创建的新目录的完整路径。还可以传入一个可选的options对象,其中可以设置权限模式或启用递归创建父目录等功能。最后一个参数是可选的回调函数,当目录创建完成后,该函数会被调用并传入错误信息(如果有)。
javascript 复制代码
const fs = require('fs');

// 异步创建目录示例
fs.mkdir('./new-directory', { recursive: true }, (err) => {
  if (err) {
    console.error('创建目录失败:', err);
  } else {
    console.log('目录创建成功: new-directory');
  }
});
  • fs.mkdirSync(path[, options]) ------ 同步创建目录。与异步版本相似,但它是同步执行并在出现错误时抛出异常。
javascript 复制代码
try {
  fs.mkdirSync('./new-directory-sync', { recursive: true });
  console.log('目录创建成功: new-directory-sync');
} catch (err) {
  console.error('创建目录失败:', err);
}

💡 fs.rmdir() & fs.rmdirSync()

  • fs.rmdir(path, callback) ------ 异步删除目录。此方法用于删除指定的空目录,若目录非空,则操作会失败。
javascript 复制代码
fs.rmdir('./empty-directory', (err) => {
  if (err) {
    console.error('删除目录失败:', err);
  } else {
    console.log('空目录删除成功: empty-directory');
  }
});
  • fs.rmdirSync(path) ------ 同步删除目录。与异步版本相同,但同步执行并在删除失败时抛出异常。
javascript 复制代码
try {
  fs.rmdirSync('./empty-directory-sync');
  console.log('空目录删除成功: empty-directory-sync');
} catch (err) {
  console.error('删除目录失败:', err);
}

另外,Node.js 还提供了诸如 fs.readdir()(异步读取目录内容)和 fs.readdirSync()(同步读取目录内容)等方法,用于枚举指定目录中的文件和子目录。同时,还有 fs.promises.readdir() 提供基于Promise的异步API。

javascript 复制代码
// 异步读取目录内容
fs.readdir('./directory', (err, files) => {
  if (err) throw err;
  console.log('目录内容:', files);
});

// 同步读取目录内容
try {
  const files = fs.readdirSync('./directory');
  console.log('目录内容:', files);
} catch (err) {
  console.error('读取目录内容失败:', err);
}

// 基于Promise的异步读取目录内容
fs.promises.readdir('./directory')
  .then(files => console.log('目录内容:', files))
  .catch(err => console.error('读取目录内容失败:', err));

借助这些方法,开发者可以在Node.js应用程序中有效地管理和操作目录结构,包括新建目录、删除空目录,以及浏览目录内文件列表。在具体应用时,同步操作适用于简单脚本或预期不会长时间阻塞的情况,而异步操作则常用于构建高性能、非阻塞的应用程序。


🌈 VI. 打开文件

💡 fs.openSync() & fs.open()

  • fs.openSync(path, flags[, mode]) ------ 同步打开文件。接受三个参数:文件路径文件打开模式文件访问模式,返回一个文件描述符
  • fs.open(path, flags[, mode], callback) ------ 异步打开文件。接受四个参数:文件路径文件打开模式文件访问模式回调函数
javascript 复制代码
const fs = require("fs")
// 同步打开文件
try {
	const fd = fs.openSync('./test-read-async.txt', 'r');
	console.log('同步打开文件openSync-File descriptor:', fd);
	fs.readFile(fd, 'utf8', (readErr, data) => {
		if (readErr) {
			console.error('Failed to read file:', readErr);
		} else {
			console.log('File content:', data);
			// 在读取完成后记得关闭文件描述符
			fs.close(fd, closeErr => {
				if (closeErr) console.error('Failed to close file:', closeErr);
			});
		}
	});
} catch (err) {
	console.error('同步打开文件出错openSync-Error opening file:', err);
}
// 异步打开文件
fs.open('./test-read-async.txt', 'r', (err, fd) => {
	if (err) {
		console.error('异步打开文件出错open-Error opening file:', err);
	} else {
		console.log('异步打开文件open-File descriptor:', fd);
		// 假设我们在这里根据文件描述符读取文件内容
		fs.readFile(fd, 'utf8', (readErr, data) => {
			if (readErr) {
				console.error('Failed to read file:', readErr);
			} else {
				console.log('File content:', data);
				// 在读取完成后记得关闭文件描述符
				fs.close(fd, closeErr => {
					if (closeErr) console.error('Failed to close file:', closeErr);
				});
			}
		});
	}
})

⚠️ 名词解释

文件描述符:文件描述符(File Descriptor)在计算机操作系统中扮演着至关重要的角色,特别是在类Unix系统(如Linux、Mac OS X等)中。文件描述符是一种轻量级的句柄,它是操作系统内部用来跟踪和管理打开文件的一种机制。它是一个整数值,用来唯一标识进程中打开的文件或其他输入/输出资源

通过文件描述符,进程可以高效地与内核交互,执行对文件的读、写、关闭以及其他高级I/O操作,无需每次都通过文件路径去访问文件

文件打开模式(flags):用于指定如何打开和处理文件。以下是常见的文件打开模式:

javascript 复制代码
/* 文件访问模式
* 'r' - 只读模式。如果文件不存在,openSync()会抛出一个错误。
* 'r+' - 读写模式。文件必须存在,否则会抛出错误。在这种模式下,你可以从文件的当前偏移位置开始读取和写入。
* 'w' - 只写模式。如果文件存在,其内容会被清空;如果文件不存在,则会被创建。
* 'w+' - 读写模式。同'w',也会清空文件内容,然后允许读取和写入。文件如果不存在会被创建。
* 'a' - 追加模式。打开文件以追加数据。如果文件不存在,会被创建。所有写入的数据都被追加到文件的末尾,不会改变已有内容。
* 'a+' - 读写追加模式。允许读取和追加数据。追加写入同'a',同时也允许从当前文件指针位置读取文件内容。
* 'x' - 创建并写入模式。如果文件已存在,则操作失败,否则创建新文件并写入。
* 'x+' - 创建并读写模式。同'x',但是也允许读取。
* 's' 和 'S'(Windows平台特有) - 打开文件并安全地同步I/O操作,防止其他进程在同一时刻访问该文件。
* */

🌟 VII. 删除文件

💡 fs.unlink() & fs.unlinkSync()

  • fs.unlink(path, callback) ------ 异步删除文件。这个方法用于删除指定的文件,接收文件路径作为参数,并提供一个回调函数用于处理可能出现的错误。
javascript 复制代码
fs.unlink('./test-delete-file.txt', (err) => {
  if (err) {
    console.error('删除文件失败:', err);
  } else {
    console.log('文件删除成功: test-delete-file.txt');
  }
});
  • fs.unlinkSync(path) ------ 同步删除文件。同步版的删除文件方法,同样接收文件路径作为参数,但在遇到错误时会抛出异常。
javascript 复制代码
try {
  fs.unlinkSync('./test-delete-file-sync.txt');
  console.log('文件删除成功: test-delete-file-sync.txt');
} catch (err) {
  console.error('删除文件失败:', err);
}

🎯 VIII. 重命名或移动文件/目录

💡 fs.rename() & fs.renameSync()

  • fs.rename(oldPath, newPath, callback) ------ 异步重命名或移动文件/目录。这两个参数分别代表当前文件路径和新的目标路径,重命名或移动完成后执行回调函数。
javascript 复制代码
fs.rename('./old-name.txt', './new-name.txt', (err) => {
  if (err) {
    console.error('重命名文件失败:', err);
  } else {
    console.log('文件重命名成功: old-name.txt -> new-name.txt');
  }
});
  • fs.renameSync(oldPath, newPath) ------ 同步重命名或移动文件/目录。与异步版本功能相同,但在重命名或移动过程中遇到错误时会立即抛出异常。
javascript 复制代码
try {
  fs.renameSync('./old-dir', './new-dir');
  console.log('目录重命名成功: old-dir -> new-dir');
} catch (err) {
  console.error('重命名目录失败:', err);
}

🗄 IX. 复制文件

由于Node.js标准库fs模块并没有直接提供复制文件的方法,但可以通过读取源文件内容后写入到目标文件实现文件复制。以下是一个简单的异步文件复制示例:

javascript 复制代码
const fsPromises = require('fs').promises;

async function copyFile(src, dest) {
  try {
    const data = await fsPromises.readFile(src);
    await fsPromises.writeFile(dest, data);
    console.log(`文件复制成功: ${src} -> ${dest}`);
  } catch (err) {
    console.error('文件复制失败:', err);
  }
}

copyFile('./source.txt', './destination.txt');

对于同步版本,可以使用fs.readFileSync()fs.writeFileSync()配合实现类似功能。


🔐 X. 文件权限及所有权更改

  • fs.chmod(path, mode, callback) & fs.chmodSync(path, mode) ------ 修改文件或目录的权限模式。
  • fs.chown(path, uid, gid, callback) & fs.chownSync(path, uid, gid) ------ 更改文件的所有者和组ID。

这两个方法主要用于调整文件系统的权限和所有权,其中mode参数采用八进制数字表示权限(如0o755),uidgid则是用户的ID和组ID。在处理这些涉及权限的操作时务必谨慎,因为不正确的操作可能导致安全问题或服务中断。


🏁 总结

Node.js 内置的 fs 模块以其强大的文件系统功能,赋予开发者对文件和目录进行全方位管理的能力。无论是处理文本文件、目录结构,还是变更权限,fs 模块都能轻松应对:

  • 读写操作fs.readFileSync()fs.readFile() 实现异步和同步读取文件,而 fs.writeFileSync(), fs.writeFile() 以及 fs.appendFileSync()fs.appendFile() 则分别用于同步与异步写入和追加内容到文件中。异步读写能有效避免 I/O 阻塞,提升程序响应速度和并发能力。

  • 文件状态查询fs.stat()fs.statSync() 可获取文件或目录的状态信息,如类型、大小、最后修改时间等。

  • 目录管理fs.mkdir(), fs.mkdirSync() 创建目录,支持递归创建父级目录;fs.rmdir(), fs.rmdirSync() 删除空目录;fs.readdir()fs.readdirSync() 则用于读取目录内容。

  • 文件删除fs.unlink()fs.unlinkSync() 用于异步和同步删除文件。

  • 文件重命名/移动fs.rename()fs.renameSync() 支持对文件或目录进行重命名或移动操作。

  • 文件复制 :尽管 fs 模块未提供直接的复制文件方法,但可以通过读取源文件内容并写入目标文件的方式来实现。

  • 权限管理fs.chmod()fs.chmodSync() 可以修改文件或目录的权限模式;fs.chown()fs.chownSync() 用于更改文件的所有者和所属组。

综合运用这些方法,Node.js 开发者可以根据实际需求灵活选择同步或异步操作,优化代码性能,确保在开发各种类型的文件系统相关应用时游刃有余。不过需要注意的是,在处理敏感操作如权限更改时,应格外小心以避免潜在的安全风险和系统稳定性问题。

相关推荐
Martin -Tang几秒前
vite和webpack的区别
前端·webpack·node.js·vite
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
吾店云建站4 小时前
WordPress 6.7 “Rollins”发布
科技·程序人生·职场和发展·创业创新·程序员创富
杜杜的man5 小时前
【go从零单排】Closing Channels通道关闭、Range over Channels
开发语言·后端·golang