开发手册 欢迎您!
软件开发者资料库

文件系统 | Node.js

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。

Node.js v8.x 中文文档


目录

fs (文件系统)#

稳定性: 2 - 稳定的

文件 I/O 是对标准 POSIX 函数的简单封装。通过 require('fs') 使用该模块。所有的方法都有异步和同步的形式。

异步方法的最后一个参数都是一个回调函数。传给回调函数的参数取决于具体方法,但回调函数的第一个参数都会保留给异常。如果操作成功完成,则第一个参数会是 nullundefined

当使用同步方法时,任何异常都会被立即抛出。可以使用 try/catch 来处理异常,或让异常向上冒泡。

异步方法的例子:

const fs = require('fs');fs.unlink('/tmp/hello', (err) => {  if (err) throw err;  console.log('成功删除 /tmp/hello');});

同步方法的例子:

const fs = require('fs');fs.unlinkSync('/tmp/hello');console.log('成功删除 /tmp/hello');

异步的方法不能保证执行顺序。所以下面的例子可能会出错:

fs.rename('/tmp/hello', '/tmp/world', (err) => {  if (err) throw err;  console.log('重命名完成');});fs.stat('/tmp/world', (err, stats) => {  if (err) throw err;  console.log(`文件属性: ${JSON.stringify(stats)}`);});

fs.stat 可能在 fs.rename 之前执行。正确的方法是把回调链起来。

fs.rename('/tmp/hello', '/tmp/world', (err) => {  if (err) throw err;  fs.stat('/tmp/world', (err, stats) => {    if (err) throw err;    console.log(`文件属性: ${JSON.stringify(stats)}`);  });});

在繁忙的进程中,建议使用异步的方法。同步的方法会阻塞整个进程,直到完成(停止所有连接)。

可以使用文件名的相对路径。路径是相对 process.cwd() 的。

大多数 fs 函数可以省略回调函数,在这种情况下,会使用默认的回调函数。若要追踪最初的调用点,可设置 NODE_DEBUG 环境变量:

注意:不建议省略异步方法的回调函数,未来的版本可能会导致抛出错误。

$ cat script.jsfunction bad() {  require('fs').readFile('/');}bad();$ env NODE_DEBUG=fs node script.jsfs.js:88        throw backtrace;        ^Error: EISDIR: illegal operation on a directory, read    

注意:在 Windows 上 Node.js 遵循单驱动器工作目录的理念。当使用驱动器路径且不带反斜杠时就能体验到该特征。例如,fs.readdirSync('c:\\') 可能返回与 fs.readdirSync('c:') 不同的结果。详见 MSDN 路径文档

注意: 在 Windows 上,使用 w 选项(通过 fs.openfs.writeFile) 打开已有隐藏文件将会失败,错误信息为 EPERM 。已有隐藏文件可以通过 r+ 选项打开。调用 fs.ftruncate 可以用来重置文件内容。

线程池的使用#

注意: 在所有的文件系统 API 中,除了 fs.FSWatcher() 和那些显式同步之外都可以使用 libuv 的线程池,这对于某些应用程序可能会产生出乎意料问题和负面的性能影响,相关详细信息,请参阅 UV_THREADPOOL_SIZE 文档。

WHATWG URL object support#

稳定性: 1 - 实验性的

对于大多数 fs 模块的函数, path 或者 filename 参数可以当作一个 WHATWG URL 对象传入。只有 URL 对象使用被支持的 file: 协议。

const fs = require('fs');const { URL } = require('url');const fileUrl = new URL('file:///tmp/hello');fs.readFileSync(fileUrl);

注意file: URLS 必须是绝对路径。

使用 WHATWG URL 对象在不同的平台会有特定的行为。

在 Windows 上, 携带主机名的 file: URLs 被转换为 UNC 路径, 而有硬盘盘符的 file: URLs 会被转换成本地绝对路径。既没有主机名,也没有盘符的 file: URLs 在转换时会抛出错误。

// 在Windows上 :// - WHATWG 标准的 URLs 会将携带主机名的 file: 转换为 UNC 路径// file://hostname/p/a/t/h/file => \\hostname\p\a\t\h\filefs.readFileSync(new URL('file://hostname/p/a/t/h/file'));// - WHATWG 标准的 URLs 会将携带本地磁盘盘符的 file: 转换为 绝对路径// file:///C:/tmp/hello => C:\tmp\hellofs.readFileSync(new URL('file:///C:/tmp/hello'));// - WHATWG 标准的 URLs 在转换内容时,如果不携带主机名,则必须包含本地磁盘盘符fs.readFileSync(new URL('file:///notdriveletter/p/a/t/h/file'));fs.readFileSync(new URL('file:///c/p/a/t/h/file'));// TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must be absolute

注意: 携带盘符的 file: URLs 必须使用 : 作为盘符后的分隔符。使用其他符号会抛出错误。

在其他所有的平台上, 都不支持携带主机名的 file: URLs,且会抛出错误。

// 在其他平台上:// - WHATWG 标准的 URLs 不支持携带 hostname 的 file: 进行转换// file://hostname/p/a/t/h/file => throw!fs.readFileSync(new URL('file://hostname/p/a/t/h/file'));// TypeError [ERR_INVALID_FILE_URL_PATH]: must be absolute// - WHATWG 标准的 URLs 会将 file: 转换绝对路径 // file:///tmp/hello => /tmp/hellofs.readFileSync(new URL('file:///tmp/hello'));

file: URL 包含已经编码的斜线符号会在所有平台抛出错误。

// 在 Windows 上fs.readFileSync(new URL('file:///C:/p/a/t/h/%2F'));fs.readFileSync(new URL('file:///C:/p/a/t/h/%2f'));/* TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must not include encoded\ or / characters */// 在 POSIX 上fs.readFileSync(new URL('file:///p/a/t/h/%2F'));fs.readFileSync(new URL('file:///p/a/t/h/%2f'));/* TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must not include encoded/ characters */

在 Windows 上, 携带已编码的反斜线 file: URLs 在编码是会抛出错误。

// 在 Windows 上fs.readFileSync(new URL('file:///C:/path/%5C'));fs.readFileSync(new URL('file:///C:/path/%5c'));/* TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must not include encoded\ or / characters */

Buffer API#

fs 函数支持传递和接收字符串路径与 Buffer 路径。后者的目的是使其可以在允许非 UTF-8 文件名的文件系统中工作。对于大多数普通用途,使用 Buffer 路径是不必要的,因为字符串 API 会自动与 UTF-8 相互转换。

注意,在某些文件系统(如 NTFS 和 HFS+),文件名总是被编码为 UTF-8。在这些文件系统中,传入非 UTF-8 编码的 Buffer 到 fs 函数将无法像预期那样工作。

fs.FSWatcher 类#

fs.watch() 返回的对象是该类型。

提供给 fs.watch()listener 回调会接收返回的 FSWatcher 的 change 事件。

该对象本身可触发以下事件:

'change' 事件#

  • eventType fs 变化的类型
  • filename | 变化的文件名(如果是相关的/可用的)

当一个被监视的目录或文件有变化时触发。详见 fs.watch()

filename 参数可能不会被提供,这依赖于操作系统支持。如果提供了 filename,则若 fs.watch() 被调用时 encoding 选项被设置为 'buffer' 则它会是一个 Buffer,否则 filename 是一个字符串。

// 例子,处理 fs.watch 监听器fs.watch('./tmp', { encoding: 'buffer' }, (eventType, filename) => {  if (filename) {    console.log(filename);    // 输出:   }});

'error' 事件#

  • error

当发生错误时触发。

watcher.close()#

停止监听 fs.FSWatcher 的变化。

fs.ReadStream 类#

ReadStream 是一个可读流

'close' 事件#

ReadStream 底层的文件描述符被关闭时触发。

'open' 事件#

  • fd 被 ReadStream 使用的整数文件描述符。

当 ReadStream 的文件被打开时触发。

readStream.bytesRead#

已读取的字节数。

readStream.path#

流正在读取的文件的路径,指定在 fs.createReadStream() 的第一个参数。如果 path 传入的是一个字符串,则 readStream.path 是一个字符串。如果 path 传入的是一个 Buffer,则 readStream.path 是一个 Buffer

fs.Stats 类#

fs.stat()fs.lstat()fs.fstat() 及其同步版本返回的对象都是该类型。

  • stats.isFile()
  • stats.isDirectory()
  • stats.isBlockDevice()
  • stats.isCharacterDevice()
  • stats.isSymbolicLink() (仅对 fs.lstat() 有效)
  • stats.isFIFO()
  • stats.isSocket()

对于一个普通文件,util.inspect(stats) 会返回一个类似如下的字符串:

Stats {  dev: 2114,  ino: 48064969,  mode: 33188,  nlink: 1,  uid: 85,  gid: 100,  rdev: 0,  size: 527,  blksize: 4096,  blocks: 8,  atimeMs: 1318289051000.1,  mtimeMs: 1318289051000.1,  ctimeMs: 1318289051000.1,  birthtimeMs: 1318289051000.1,  atime: Mon, 10 Oct 2011 23:24:11 GMT,  mtime: Mon, 10 Oct 2011 23:24:11 GMT,  ctime: Mon, 10 Oct 2011 23:24:11 GMT,  birthtime: Mon, 10 Oct 2011 23:24:11 GMT }

注意atimeMsmtimeMsctimeMsbirthtimeMs 是以单位为毫秒保存相对应时间的数字 numbers。他们的精度由所在的平台决定。atimemtimectime 以及 birthtime 是表示各个时间的日期对象 [Date][MDN-Date]Date 与数值并没有关联。对数值进行重新赋值,或者改变 Date 的值,不会反映到相对应的表示中。

Stat 时间值#

stat 对象中的时间有以下语义:

  • atime "访问时间" - 文件数据最近被访问的时间。会被 mknod(2)utimes(2)read(2) 系统调用改变。
  • mtime "修改时间" - 文件数据最近被修改的时间。会被 mknod(2)utimes(2)write(2) 系统调用改变。
  • ctime "变化时间" - 文件状态最近更改的时间(修改索引节点数据)会被 chmod(2)chown(2)link(2)mknod(2)rename(2)unlink(2)utimes(2)read(2)write(2) 系统调用改变。
  • birthtime "创建时间" - 文件创建的时间。当文件被创建时设定一次。在创建时间不可用的文件系统中,该字段可能被替代为 ctime1970-01-01T00:00Z(如 Unix 的纪元时间戳 0)。注意,该值在此情况下可能会大于 atimemtime。在 Darwin 和其它的 FreeBSD 衍生系统中,如果 atime 被使用 utimes(2) 系统调用显式地设置为一个比当前 birthtime 更早的值,也会有这种情况。

在 Node.js v0.12 之前的版本中,ctime 在 Windows 系统中保存 birthtime。注意,在 v0.12 中,ctime 不是“创建时间”,并且在 Unix 系统中,它从来都不是。

fs.WriteStream 类#

WriteStream 一个可写流

'close' 事件#

WriteStream 底层的文件描述符被关闭时触发。

'open' 事件#

  • fd 被 WriteStream 使用的整数文件描述符。

当 WriteStream 的文件被打开时触发。

writeStream.bytesWritten#

已写入的字节数。不包括仍在排队等待写入的数据。

writeStream.path#

流正在写入的文件的路径,指定在 fs.createWriteStream() 的第一个参数。如果 path 传入的是一个字符串,则 writeStream.path 是一个字符串。如果 path 传入的是一个 Buffer,则 writeStream.path 是一个 Buffer

fs.access(path[, mode], callback)#

  • path | |
  • mode Default: fs.constants.F_OK
  • callback
    • err

测试 path 指定的文件或目录的用户权限。mode 是一个可选的整数,指定要执行的可访问性检查。以下常量定义了 mode 的可能值。可以创建由两个或更多个值的位或组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK)。

  • fs.constants.F_OK - path 文件对调用进程可见。这在确定文件是否存在时很有用,但不涉及 rwx 权限。如果没指定 mode,则默认为该值。
  • fs.constants.R_OK - path 文件可被调用进程读取。
  • fs.constants.W_OK - path 文件可被调用进程写入。
  • fs.constants.X_OK - path 文件可被调用进程执行。对 Windows 系统没作用(相当于 fs.constants.F_OK)。

最后一个参数 callback 是一个回调函数,会带有一个可能的错误参数被调用。如果可访问性检查有任何的失败,则错误参数会是一个 Error 对象。下面的例子会检查 /etc/passwd 文件是否可以被当前进程读取和写入。

fs.access('/etc/passwd', fs.constants.R_OK | fs.constants.W_OK, (err) => {  console.log(err ? 'no access!' : 'can read/write');});

不建议在调用 fs.open()fs.readFile()fs.writeFile() 之前使用 fs.access() 检查一个文件的可访问性。如此处理会造成紊乱情况,因为其他进程可能在两个调用之间改变该文件的状态。作为替代,用户代码应该直接打开/读取/写入文件,当文件无法访问时再处理错误。

例子:

写入(不推荐)

fs.access('myfile', (err) => {  if (!err) {    console.error('myfile already exists');    return;  }  fs.open('myfile', 'wx', (err, fd) => {    if (err) throw err;    writeMyData(fd);  });});

写入(推荐)

fs.open('myfile', 'wx', (err, fd) => {  if (err) {    if (err.code === 'EEXIST') {      console.error('myfile already exists');      return;    }    throw err;  }  writeMyData(fd);});

读取(不推荐)

fs.access('myfile', (err) => {  if (err) {    if (err.code === 'ENOENT') {      console.error('myfile does not exist');      return;    }    throw err;  }  fs.open('myfile', 'r', (err, fd) => {    if (err) throw err;    readMyData(fd);  });});

读取(推荐)

fs.open('myfile', 'r', (err, fd) => {  if (err) {    if (err.code === 'ENOENT') {      console.error('myfile does not exist');      return;    }    throw err;  }  readMyData(fd);});

以上不推荐的例子检查可访问性之后再使用文件;推荐的例子更好,因为它们直接使用文件并处理任何错误。

通常,仅在文件不会被直接使用时才检查一个文件的可访问性,例如当它的可访问性是来自另一个进程的信号。

fs.accessSync(path[, mode])#

  • path | |
  • mode Default: fs.constants.F_OK
  • 返回: undefined

同步地测试 path 指定的文件或目录的用户权限。 mode 是一个可选的整数,指定要执行的可访问性检查。 以下常量定义了 mode 的可能值。 可以创建由两个或更多个值的位或组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK)。

  • fs.constants.F_OK - path 文件对调用进程可见。 这在确定文件是否存在时很有用,但不涉及 rwx 权限。 如果没指定 mode ,则默认为该值
  • fs.constants.R_OK - path 文件可被调用进程读取。
  • fs.constants.W_OK - path 文件可被调用进程写入。
  • fs.constants.X_OK - path 文件可被调用进程执行。 对 Windows 系统没作用(相当于 fs.constants.F_OK)。

如果可访问性检查有任何的失败,则错误参数会是一个 Error 对象。 否则返回 undefined

try {  fs.accessSync('etc/passwd', fs.constants.R_OK | fs.constants.W_OK);  console.log('can read/write');} catch (err) {  console.error('no access!');}

fs.appendFile(file, data[, options], callback)#