nodejs:原子文件替换操作,只触发一次一些观察者

时间:2013-11-22 19:45:14

标签: javascript node.js watch

我有两种流程。一个人应该写一个文件,其他人应该阅读它,如果它被改变了。

如果他们必须重新加载文件,我找到fs.watch来通知阅读流程。

要编写文件,我可以使用fs.writeFile

但我不确定,如果这是原子替代品。如果文件很大,watch会多次触发吗?

将此作为原子操作的正确方法是什么?

仅供参考:在目标系统上运行Linux。

1 个答案:

答案 0 :(得分:1)

经过一些测试后我得到了这个结果:

在写入大文件时,

fs.watch会多次触发change事件。问题是,您无法看到写入操作已完成的最后一个问题。

解决方案是:将临时文件写在同一目录中,而不是使用linkunlink将旧文件替换为新文件。

在代码中它看起来像这样:

var fs = require("fs");
var path = require("path");
var consts = require('constants');

/**
 * write given date in temp file and replace the dstpath with that.
 *
 * @author Tobias Lindig
 * 
 * @param  {string} dstpath destination path
 * @param  {string} data    [description]
 *
 * @throws {Error} If fs operations failed.
 */
function atomicWrite(dstpath, data) {
    // build a unique temp path
    var dir = path.dirname(dstpath);
    var tempName = [
        'temp',
        process.pid,
        (Math.random() * 0x1000000000).toString(36),
        new Date().getTime()
    ].join('-');
    var tempPath = path.join(dir, tempName);

    var tempOptions = {
        encoding: 'utf8',
        mode: 420, // aka  0644 in Octal
        flags: consts.O_CREAT | consts.O_TRUNC | consts.O_RDWR | consts.O_EXCL
    };

    //local function to remove temp file
    var fn_removeTempFileSync = function() {
        try {
            fs.unlinkSync(tempPath);
        } catch (ex) {
            // ignore fail, may be file was not created.
        }
    };

    try {
        fs.writeFileSync(tempPath, data, tempOptions);

        // remove possible old version of file
        // because fs.link can not overwrite existing dstpath.
        if (fs.existsSync(dstpath)) {
            fs.unlinkSync(dstpath);
        }

        //copy temp file to destination path
        fs.linkSync(tempPath, dstpath);

        fn_removeTempFileSync();
    } catch (ex) {
        fn_removeTempFileSync();
        throw ex;
    }
}

现在fs.watch仅为文件(“dstpath”)触发事件change一次。

那个理论。

但是在现实世界中,遗憾的是change事件并非在每种情况下被触发,有时它错过了。所以我也看了rename事件。

该事件将按此顺序排列:

rename //曾经,如果文件被删除了 rename //永远,文件已创建 change //有时,文件已创建

要只读取一次文件,我会成员mtime的文件并且只读,如果不同的话。