fs模块(一)

FS

FS 是file system的缩写,fs 模块可以实现与硬盘的交互,例如文件的创建、删除、重命名、移动,还有文件内容的写入、读取,以及文件夹的相关操作。在 Node.js 种,fs 模块提供了异步和同步两种方式操作文件。

基本使用

writeFile

函数定义
fs.writeFile(filename, data[, options], callback)

在编程中,[, options] 这种语法通常用来表示函数或方法的参数是可选的。这种语法在多种编程语言的文档中常见,特别是在描述函数签名时。

  • 方括号 [] 内的参数意味着这个参数不是调用函数时必须提供的,它有一个默认值或者在函数内部被忽略,如果调用者没有显式地提供它。
    data[, options] 这种写法时,data 是必需参数。
参数
  • filename:要写入数据的文件的路径,这是一个必需参数。
  • data:要写入文件的数据,可以是字符串、Buffer 或者是一个包含这些类型的数组,这也是一个必需参数。
  • options:这是一个可选参数,可以是一个对象,用于指定编码、文件模式等选项。如果省略,Node.js 会使用默认值。options 对象可以包含以下属性:
    • encoding:指定数据的字符编码。如果 data 是 Buffer,则此选项会被忽略。
    • mode:设置文件的权限。默认值是 0o666,并且这个值会受到进程的 umask 影响。
    • flag:指定文件操作的模式。默认是 'w',表示写入模式,如果文件存在则覆盖。
  • callback:这是一个可选的回调函数,当数据写入完成后被调用。回调函数没有参数,或者有一个可能表示写入过程中发生的错误的参数。
示例
异步写入

异步写入不会阻塞其他操作,执行完毕后通过回调函数通知结果。这是在 Node.js 中推荐的做法,因为它避免了阻塞事件循环,尤其是在处理大文件或网络 I/O 时更为重要。

const fs = require('fs');

fs.writeFile('./fs.txt', 'Hello, Node.js!','utf8',err=>{
    if(err) {
        console.log('写入失败', err);
        return;
    }else {
        console.log('写入成功')
    }
})

在这个例子中,fs.writeFile()接收四个参数:文件路径、要写入的内容、文件编码(默认为'utf8',这里显式指定)以及一个回调函数,如果写入过程发生错误,回调函数的第一个参数会接收到错误信息;如果没有错误,则第二个参数会是null,表示写入成功。
展示一个示例模拟在没有写入权限的情况下尝试异步写入文件,并捕获及打印错误信息。假设我们尝试向一个需要管理员权限才能写入的位置写入文件,这通常会导致权限错误。

const fs = require('fs');

// 假设一个需要较高权限才能写入的路径
const restrictedPath = '/user/restricted/path/example.txt';

// 要写入的内容
const content = 'Hello, Node.js!';

// 使用fs.writeFile()进行异步写入,并捕获可能的错误
fs.writeFile(restrictedPath, content, 'utf8', (err) => {
  if (err) {
    // 打印错误信息
    console.error('写入文件时遇到错误:', err);
  } else {
    console.log('文件写入成功');
  }
});

在这个示例中,如果运行此脚本的用户没有对/user/restricted/path/目录的写入权限,fs.writeFile()将会在回调函数中返回一个错误对象。错误对象通常会包含错误码和描述信息,可以帮助我们识别问题所在。错误输出可能类似于:

写入文件时遇到错误: { Error: EACCES: permission denied, open '/some/restricted/path/example.txt'
  errno: -13,
  code: 'EACCES',
  syscall: 'open',
  path: '/some/restricted/path/example.txt' }

这个错误信息表明了错误类型为EACCES,即权限被拒绝,同时还提供了其他详细信息如错误号(errno)、发生错误的系统调用(syscall)和相关的文件路径。

同步写入

虽然同步写入在某些特定场景下可能更直观,但请注意它会阻塞其他操作直到完成,这在Node.js中通常不鼓励使用,因为它会影响程序的整体性能和响应性。

const fs = require('fs');

// 文件路径
const filePath = 'example.txt';

// 要写入的内容
const content = 'Hello, Node.js!';

// 使用fs.writeFileSync()方法进行同步写入
try {
  fs.writeFileSync(filePath, content, 'utf8');
  console.log('文件写入成功');
} catch (err) {
  console.error('写入文件时出错:', err);
}

这里,fs.writeFileSync()同样接收文件路径、内容和编码作为参数,但它会立即执行并阻塞后续代码,直到写入完成。如果发生错误,会抛出异常,因此需要使用try...catch语句来捕获并处理错误。

Q:如何理解同步异步?
A:现在用js定时器来简单理解一下同步和异步

// 实际上Node.js没有“同步定时器”的概念,但为了对比,我们构想一个理论上的场景
function synchronousTimer(ms) {
  // 假设的同步等待ms毫秒
  // 在这个等待期间,线程会被阻塞,无法执行其他任务
  // Node.js实际上没有这样的同步定时功能,这里在模拟同步
  const start = Date.now();
  while (Date.now() - start < ms) {}
  console.log("时间到了!");
}

console.log("开始计时...");
synchronousTimer(2000); // 理论上如果这是同步的,会阻塞2秒
console.log("计时结束!"); // 这句话只有在“定时器”结束后才会打印

异步定时器, setTimeoutsetInterval

console.log("开始计时...");

setTimeout(function() {
  console.log("时间到了!");
}, 2000); // 2秒后执行回调

console.log("计时设置完成,但不等待,继续执行...");

在这个Node.js示例中,setTimeout函数安排了一个回调函数在2秒后执行,但是它立即返回,不会阻塞当前执行流程。因此,"计时设置完成,但不等待,继续执行…“这一行代码会立刻打印出来,然后过了2秒后,才会打印"时间到了!”。

  • 异步性: setTimeout的非阻塞性质体现了Node.js的异步处理方式,它允许程序在等待某个事件(这里是时间到达)时继续执行其他任务,而不是原地等待。
  • 事件循环: 这种机制依赖于Node.js的事件循环,它会在适当的时候(即设定的时间过去后)将回调函数加入到待处理队列,从而在不阻塞主线程的前提下执行。

appendFile

fs.appendFile是Node.js模块中的一个方法,用于在现有文件的末尾追加内容,如果文件不存在则会创建新文件。这个方法非常适合记录日志或累计数据的场景,因为它不会覆盖原有文件内容。

基本语法:
fs.appendFile(file, data[, options], callback)
参数说明:
  • flie:必填,一个字符串,表示要写入的文件路径。
  • data: 必填,要追加到文件的数据。如果是字符串,则会按照options.encoding(默认为utf8)进行编码。
  • options: 可选,一个对象,可以包含以下属性:
    • encoding: 指定写入数据的字符编码,默认utf8
    • mode:指定文件权限,默认为0o666且会受到umask的影响。
    • flag:指定文件打开方式,默认为a(追加模式)。
  • callback: 当使用回调风格时必填,一个函数,当操作完成时调用,传递可能的错误作为第一个参数。

A:解释一下mode

mode(文件模式):在Linux和类Unix系统中,文件模式(或文件权限)是一个用来控制谁可以读、写或执行文件的系统。文件权限通常用八进制数表示,每个数字代表不同的权限级别:
4:可读
2:可写
1:可执行
例如,0o666 表示:
第一个数字 6 代表文件所有者拥有读和写权限(4+2=6)
第二个数字 6 代表与文件所有者同组的用户拥有读和写权限
第三个数字 6 代表其他用户也拥有读和写权限

A :解释一下umask

umask(用户文件创建掩码):是一个系统设置,用于决定新创建的文件和目录的默认权限。它是通过从最大权限(通常是 0o777 即所有权限)中减去一个值来工作的。
例如,如果 umask 设置为 0022:
对于文件,最大权限 0o777 减去 umask 的 0022 得到 0o755。这意味着新创建的文件默认权限是所有者有全部权限(读、写、执行),而同组用户和其他用户只有读和执行权限。
对于目录,最大权限 0o777 减去 umask 的 0022 得到 0o755。这意味着新创建的目录默认权限是所有者有全部权限,而同组用户和其他用户只有读和执行权限,但不允许其他人在此目录下创建新文件或目录。

A:解释一下node.js下的mode

fs.appendFile 中的 mode
在Node.js的fs.appendFile函数中,mode参数允许你指定新创建文件的权限,如未指定mode,Node.js将使用默认的0o666。然而,实际的文件权限还会收到进程的umask影响,这意味着最终的文件权限将是0o666减去umask的值。
例如:
umask是0022,那么即使fs.appendFile使用的是默认的0o666模式,实际的文件权限将是0o644(0o666 - 0022),这意味着所有者可以读写权限,但其他用户只读文件。

示例代码:
回调风格
const fs = require('fs');

fs.appendFile('example.txt', '这是追加的内容\n', (err) => {
  if (err) {
    console.error('追加文件时出错:', err);
  } else {
    console.log('内容已成功追加到文件!');
  }
});
Promise风格
const fs = require('fs').promises;

async function appendContentToFile() {
  try {
    await fs.appendFile('example.txt', '这是通过Promise追加的内容\n');
    console.log('内容已成功追加到文件!');
  } catch (err) {
    console.error('追加文件时出错:', err);
  }
}

appendContentToFile();
fs.writeFile风格追加内容
const fs = require('fs');

fs.writeFile('example.txt', '这是追加内容\n', {flag: 'a'},(err)=> {
    if (err) {
    console.error('追加文件时出错:', err);
  } else {
    console.log('内容已成功追加到文件!');
  }
})

createWriteStream

fs.createWriteStream是Node.js中fs模块提供的一个函数,用于创建一个写入流(Writable Stream),可以向文件中写入数据。**程序打开一个文件是需要消耗资源的,流式写入可以减少打开关闭文件的次数,**这个函数非常适合处理需要写入大量数据的任务,从而避免一次性将所有数据加载到内存中。
文件写入在计算机中是一个非常常见的操作,下面的场景都用到了文件写入。

  • 下载文件
  • 安装软件
  • 保存程序日志,如git
  • 编辑器保存文件
  • 视频录制

当需要持久化保存数据的时候,应该相当文件写入。在计算机编程中,“流式”(Stream)是一种数据处理方式,它允许程序以连续的方式读取或写入数据,而不需要一次性将所有数据加载到内存中。

函数定义

fs.createWriteStream(path[, options])

参数
  • path:要写入数据的文件的路径。可以是绝对路径或相对路径。
  • options:一个可选对象,包含以下属性:
    • flags:文件操作标志。默认值为 'w',表示写入模式。其他选项包括 'a'(追加模式)等。
    • encoding:指定写入数据的字符编码,默认为 'utf8'
    • fd:文件描述符,如果提供了这个值,createWriteStream 将使用这个描述符而不是打开一个新的文件。
    • mode:设置文件的权限,默认值是 0o666,并且这个值会受到进程的 umask 影响。
    • start:指定文件开始写入的位置。如果文件不存在,将从位置 0 开始写入;如果文件已存在,这个值表示从文件的哪个位置开始写入。
返回值

fs.createWriteStream 返回一个 stream.Writable 类的实例,这个实例是 Node.js 流接口的一部分,提供了写入数据的方法和事件。

流的方法
  • write(chunk[, endcoding][, callback]):向流中写入数据,chunk是要写入的数据快,encoding是数据的字符编码,callback是写入完成后的回调函数。
  • end([callback]):结束写入流,如果提供了回调函数,当流关闭时将会被调用。
流的事件
  • open:当写入流成功打开文件时触发。
  • close:当写入流关闭时触发。
  • error:如果在写入过程中发生错误,将触发此事件。
  • drain:当写入流的缓冲区被清空,可以继续写入数据时触发。
  • finish:当所有写入操作完成,并且没有更多的写入操作时触发。
示例
// createWriteStream
const fs = require("fs");

const ws = fs.createWriteStream('./流式写入.txt');

ws.write('从前从前\n');
ws.write('有个人爱你很久\n');

ws.end()

// 完整参数
const fs = require('fs');
const path = 'output.txt';

// 创建一个写入流
const writeStream = fs.createWriteStream(path, {
  flags: 'a', // 追加模式
  encoding: 'utf8', // 使用 utf8 编码
  mode: 0o644, // 设置文件权限
});

// 写入数据
writeStream.write('Hello, Node.js!', 'utf8', () => {
  console.log('数据已写入');
});

// 结束写入
writeStream.end(() => {
  console.log('写入流已关闭');
});

Q:end是必须的吗?
A:不是,但建议写上。
在 Node.js 的流式接口中,end 方法用于结束可写流(Writable Stream)的写入操作。调用 end 方法是可选的,但通常推荐使用它,原因如下:

  • 结束信号:end 方法告诉流,没有更多的数据要写入了。这对于流知道何时关闭或完成其操作是很重要的。
  • 触发 finish 事件:调用 end 方法会触发 finish 事件,你可以监听这个事件来执行一些清理工作或后续操作。
const fs = require("fs");

const ws = fs.createWriteStream('./流式写入.txt');

// 监听 `finish` 事件
ws.on('finish', () => {
    console.log('所有数据已写入,写入操作完成。');
  });

ws.write('从前从前\n');
ws.write('有个人爱你很久\n');

ws.end();
  • 资源释放:对于某些类型的流,如文件写入流,调用 end 方法可以确保文件被正确关闭,释放系统资源。
  • 避免内存泄漏:如果你不调用 end,流可能会保持打开状态,这可能导致内存泄漏或其他问题。

然而,有一些情况下,你可能不需要显式调用·end

  • 自动结束:如果你使用的是 fs.createWriteStream 并且没有提供任何数据给流,流会自动结束。
  • 流已关闭:如果流已经因为其他原因(如发生错误)关闭,就不需要再次调用 end。
  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值