在 Node.js 中事务性地写入文件

Posted

技术标签:

【中文标题】在 Node.js 中事务性地写入文件【英文标题】:Transactionally writing files in Node.js 【发布时间】:2013-06-07 12:46:17 【问题描述】:

我有一个 Node.js 应用程序,它将一些配置数据存储在一个文件中。如果您更改某些设置,配置文件将写入磁盘。

目前,我使用的是简单的fs.writeFile

现在我的问题是:当 Node.js 在写入文件时崩溃会发生什么?是否有机会在磁盘上有损坏的文件?还是Node.js保证文件是以原子方式编写的,这样无论是旧版本还是新版本都有效?

如果没有,我该如何实施这样的保证?有没有这方面的模块?

【问题讨论】:

【参考方案1】:

在写入文件时 Node.js 崩溃会发生什么?是 有机会在磁盘上有损坏的文件吗?或者 Node.js 保证文件是以原子方式写入的,这样要么 旧版还是新版有效?

Node 仅在系统调用上实现了一个(薄)异步包装器,因此它不提供任何关于写入原子性的保证。事实上,fs.writeAll 反复调用fs.write,直到所有数据都被写入。你是对的,当 Node.js 崩溃时,你最终可能会得到一个损坏的文件。

如果没有,我该如何实施这样的保证?有没有这方面的模块?

我能想出的最简单的解决方案是使用的解决方案,例如对于 FTP 上传:

    将内容保存到具有不同名称的临时文件中。 将内容写入磁盘后,将临时文件重命名为目标文件。

man page 表示重命名 保证保留 newpath 的实例(在 Linux 或 OSX 等 Unix 系统上)。

【讨论】:

我可以确认在 OSX 中这并不能完全解决问题,但是它确实减少了文件损坏的发生率【参考方案2】:

fs.writeFile,就像fs 模块中的所有其他方法一样,都是作为标准POSIX 函数的简单包装器实现的(如docs 中所述)。

深入研究 nodejs 的代码,可以看到定义了所有包装器的 fs.js 对其所有文件系统调用都使用了 fs.c。更具体地说,write 方法用于写入缓冲区的内容。事实证明,POSIX specification for write 明确表示:

原子/非原子:如果写入的全部量,则写入是原子的 一个操作不会与来自任何其他进程的数据交错。 当有多个写入器将数据发送到一个 单读。应用程序需要知道一个写请求可以有多大 预计以原子方式执行。这个最大值称为 PIPE_BUF。本卷 IEEE Std 1003.1-2001 并未说明是否 超过 PIPE_BUF 个字节的写入请求是原子的,但需要 PIPE_BUF 或更少字节的写入应该是原子的。

所以看起来写起来很安全,只要缓冲区的大小小于 PIPE_BUF。这是一个依赖于系统的常量,因此您可能需要在其他地方检查它。

【讨论】:

根据manpages.courier-mta.org/htmlman7/pipe.7.html,PIPE_BUF 要求至少为 512 字节,在 Linux 中为 4.096 字节。感谢您指出这一点,因为这是非常好的和有趣的知识:-)【参考方案3】:

write-file-atomic 将满足您的需求。它写入临时文件,然后重命名。这很安全。

【讨论】:

以上是关于在 Node.js 中事务性地写入文件的主要内容,如果未能解决你的问题,请参考以下文章

在 Node.js 中写入文件时创建目录

Node.js的Buffer写入

node.js怎么往文本文档中写入数据

使用 Node.js 将对象写入文件

使用 Node.js 将对象写入文件

Node.js学习之路05——fs文件系统之文件的写入和读取