系统性学习Node.js(5)—手写 fs 核心方法

背景fs 是 Node 里用来进行文件操作的核心模块 , 这篇文章的目的是手写一些常用的 api 。
这次手写的方法是 writeFile、readFile、appendFile、copyFile , 开始手写之前 , 我们要先了解以下几个基础 Api:

  • fs.open:打开一个文件
  • fs.close:关闭一个文件
  • fs.read:读取文件
  • fs.write:写入文件
为什么要先了解这些 Api 呢 , 这就好比把大象放进冰箱需要几步?
  1. 打开冰箱 (打开文件 fs.open)
  2. 把大象放进冰箱 (读取文件/写入文件 fs.read/fs.write)
  3. 关闭冰箱 (关闭文件 fs.close)
fs.openfs.open(path[, flags[, mode]], callback)
打开一个文件 。 对文件进行操作之前都要先打开文件 。
参数解读:
  • path:文件路径
  • flags:文件系统标志 , 默认值:'r' 。 意思是要对文件进行什么操作 , 常见的有以下几种:
  • r:打开文件用于读取
  • w:打开文件用于写入
  • a:打开文件用于追加
  • mode:文件操作权限 , 默认值:0o666(可读写) 。
  • callback:回调函数 。 函数上携带的参数如下:
  • err:如果失败 , 则值为错误原因
  • fd(number):文件描述符 , 读取、写入文件时都要用到这个值
fs.closefs.close(fd, callback)
关闭一个文件 。 文件打开并操作完成后都要关闭文件 , 以释放内存 。
参数解读
  • fd:要关闭的文件描述符
  • callback:文件关闭时的回调
  • err
fs.readfs.read(fd, buffer, offset, length, position, callback)
读取文件 。 readFile 就是基于此方法实现的 。
参数解读
  • fd:要读取的文件描述符
  • buffer:数据要被写入的 buffer(将读取到的文件内容写入到此 buffer 内)
  • offset:buffer 中开始写入的偏移量(从 buffer 的第几个索引开始写入)
  • length:读取的字节数(从文件中读取几个字节)
  • postion:指定从文件中开始读取的位置(从文件的第几个字节开始读)
  • callback:回调函数
  • err
  • bytesRead:实际读取的字节数
fs.write写入文件 。 writeFile 基于此方法实现 。
fs.write(fd, buffer[, offset[, length[, position]]], callback)
  • fd:要被写入的文件描述符
  • buffer:将指定 buffer 的内容写入到文件中去
  • offset:指定 buffer 的写入位置(从 buffer 的第 offset 个索引读取内容写入到文件中去)
  • length:指定要写入的字节数
  • postion:文件的偏移量(从文件的第 position 个字节开始写入)
淦划水完毕 , 进入正题 。
readFile 方法实现我们先看一下原生方法的使用
fs.readFile('../a.js', function(err, file) {
console.log(file) // 输出的是一个存储文件二进制的 buffer 对象
})
fs.readFile('../a.js', { encoding: 'utf8' }, function(err, file) {
console.log(file) // 输出的是一个字符串
})
第一个参数是文件路径 , 第二个参数是读取完成后的回调 , 回调内可以获取文件内容 。
然后开始实现这个方法 。
按照把大象装进冰箱的步骤来实现:
1. 打开冰箱const readFile = (path, options, cb) => {
// 尝试获取 cb , 如果获取不到则抛出错误
// 由于 options 是非必填参数 , 所以它有可能是回调函数
cb = maybeCallback(cb || options)
// 获取 options , 如果未穿 options , 则取默认参数
options = getOptions(options, { flag: 'r' })
// 打开文件 ,
// 如果打开失败则直接调用 cb , 传入失败原因