掌握Node.js核心模块 - 文件系统 & 文件模块 | @RisingStack

原文链接:https://blog.risingstack.com/mastering-the-nodejs-core-modules-file-system-fs-module/

在这篇文章中,我们会看一下(Node.js的)文件系统模块,文件流和一些可选的文件模块。


想象一下这样的场景,你必须使用Node.js去完成一项任务…..

通过查官方文档,Google搜索解决方案或者在npm找一个可以解决这问题的模块,这任务看起来还是挺容易完成的。

嘛,这当然是可以的,但有时候核心模块就可以轻松地帮你完成工作(而不需要去查资料)。

通过《掌握Node.js核心模块》系列文章,你可以学习到核心模块中一些隐藏和不为人知的特性。我们也会推荐一些拓展核心模块功能的模块,对你日常的开发有极大的帮助。

Node.js 文件 模块

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

异步的API

1
2
3
4
5
6
7
8
9
// the async api
const fs = require('fs')

fs.unlink('/tmp/hello', (err) => {
if (err) {
return console.log(err)
}
console.log('successfully deleted /tmp/hello')
})

当你开发生产代码的时候,你应该使用异步的API,因为这不会阻止事件循环,使程序更加高效。

同步的API

1
2
3
4
5
6
7
8
9
10
// the sync api
const fs = require('fs')

try {
fs.unlinkSync('/tmp/hello')
} catch (ex) {
console.log(err)
}

console.log('successfully deleted /tmp/hello');

你只应该在写demo或者小的CLI时才使用同步API。

Node.js文件流

我们很少看到程序员使用文件流。

(然而)流在Node.js中是一件强大的武器,可以使程序占用更少的内存。

来看推特

在Node.js中什么是文件流?

若要处理数据,流在Node.js中是最好的。你需要理解这三个概念:

  • source - 数据来源对象(译者注:理解为水的来源),
  • pipeline - 数据流向的过程(你可以在这过滤或者更新数据。译者注:理解为水管),
  • sink - 数据最后存放的地方(译者注:理解为开了水龙头后接水的容器)。

如果需要知道更多知识,可以看下Substack的Stream Handbook.

你可以轻松地使用流复制文件,尽管文件模块不提供相应的功能:

1
2
3
4
5
6
// copy a file
const fs = require('fs')
const readableStream = fs.createReadStream('original.txt')
var writableStream = fs.createWriteStream('copy.txt')

readableStream.pipe(writableStream)

你可能会问:为什么我要这么做,不就是一个cp命令吗?

使用流去处理文件,最大的好处是在此过程中你可以轻松地加工文件,下面是一个压缩文件的例子:

1
2
3
4
5
6
const fs = require('fs')  
const zlib = require('zlib')

fs.createReadStream('original.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('original.txt'))

Node.js支持

需要从npm中得到相应模块的帮助吗?

Learn more

什么时候不要使用fs.access

使用fs.access的目的是为了检查指定文件或者目录的用户权限,例子如下:

1
2
3
4
5
6
fs.access('/etc/passwd', fs.constants.R_OK | fs.constants.W_OK, (err) => {  
if (err) {
return console.error('no access')
}
console.log('access for read/write')
})

可供权限检查的常量:

  • fs.constants.F_OK - 检查该文件在进程中是否可见,
  • fs.constants.R_OK - 检查该文件在进程中是否可读,
  • fs.constants.W_OK - 检查该文件在进程中是否可写,
  • fs.constants.X_OK - 检查该文件在进程中是否可执行。

在使用fs.open之前,可使用fs.access去检查文件的可访问性,但 fs.readFilefs.writeFile不推荐(使用fs.access去检查)。

理由很简单,如果你这么做,就会引入了一个竞态条件。在查询权限和操作文件中相互竞争,另外的进程可能已经修改了那个文件。

相反,你应该直接打开这文件并在这处理可能出现的错误。

慎用fs.watch

通过使用fs.watch,当(被监听的)文件或者目录有变化时,你会收到通知。

然而,fs.watch这个API无法跨平台百分百一致,在某些系统中根本就不可用。

注意:只有OS X和Windows系统支持递归选项,Linux不支持。

还有,回调函数中的fileName参数只在Linux和Windows系统中提供,因此你应该准备好相应的回退机制以防止它是undefined

1
2
3
4
5
fs.watch('some/path', (eventType, fileName) => {  
if (!filename) {
//filename is missing, handle it gracefully
}
})

npm中有用的文件模块

社区中维护着一些拓展了文件系统功能的模块。

graceful-fs

graceful-fs是文件系统模块的替代模块,它优化了以下内容(译者注:由于存在太多专业名词,下面的不作翻译,请见谅):

  • queues up open and readdir calls, and retries them once something closes if there is an EMFILE error from too many file descriptors,

  • ignores EINVAL and EPERM errors in chown, fchown or lchown if the user isn’t root,

  • makes lchmod and lchown become noops, if not available,

  • retries reading a file if read results in EAGAIN error.

你可以像使用文件模块一样使用它,或者都引入它们,根据需要二选一使用。

1
2
3
4
5
6
7
// use as a standalone module
const fs = require('graceful-fs')

// patching the global one
const originalFs = require('fs')
const gracefulFs = require('graceful-fs')
gracefulFs.gracefulify(originalFs)

mock-fs

mock-fs可以在文件系统模块中模拟数据,这可以让你使用模拟的文件或者目录进行测试。

用起来非常简单,请看下面的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const mock = require('mock-fs')  
const fs = require('fs')

mock({
'path/to/fake/dir': {
'some-file.txt': 'file content here',
'empty-dir': {}
},
'path/to/some.png': new Buffer([8, 6, 7, 5, 3, 0, 9])
})

fs.exists('path/to/fake/dir', function (exists) {
console.log(exists)
// will output true
})

lockfile

文件锁定是在同一时间只允许一个进程去操作文件。这可以避免产生竞态条件。

通过lockfile添加文件锁是非常简单的:

1
2
3
4
5
6
7
8
9
10
11
12
const lockFile = require('lockfile')

lockFile.lock('some-file.lock', function (err) {
// if the err happens, then it failed to acquire a lock.
// if there was not an error, then the file was created,
// and won't be deleted until we unlock it.

// then, some time later, do:
lockFile.unlock('some-file.lock', function (err) {

})
})

总结

我希望这篇文章对Node.js的文件系统讲解对你有用。

如果你对这方面内容有问题,请在下面的评论中告诉我。