【问题标题】:Creating a file only if it doesn't exist in Node.js仅当 Node.js 中不存在文件时才创建文件
【发布时间】:2012-10-05 15:06:59
【问题描述】:

我们有一个要写入文件的缓冲区。如果文件已经存在,我们需要增加一个索引,然后再试一次。有没有办法只在文件不存在时创建文件,或者我应该只统计文件,直到出现错误以找到一个不存在的文件?

例如,我有文件 a_1.jpga_2.jpg。我想我的方法尝试创建a_1.jpga_2.jpg,失败,最后成功创建a_3.jpg

理想的方法如下所示:

fs.writeFile(path, data, { overwrite: false }, function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
});

或者像这样:

fs.createWriteStream(path, { overwrite: false });

node 的 fs 库中是否存在类似的内容?

编辑:我的问题不是是否有单独的函数来检查是否存在。就是这样:如果文件不存在,有没有办法在单个文件系统调用中创建文件?

【问题讨论】:

标签: node.js


【解决方案1】:

正如您的直觉所猜到的那样,使用一对 exists / writeFile 调用的幼稚解决方案是错误的。异步代码以不可预知的方式运行。在特定情况下是

  • 有文件a.txt吗? — 没有。
  • (文件a.txt 由另一个程序创建)
  • 如果可能的话,写信给a.txt。 — 好的。

但是,是的,我们可以在一个电话中做到这一点。我们正在使用文件系统,因此最好阅读fs 上的开发人员手册。嘿,here's 很有趣。

'w' - 打开文件进行写入。文件已创建(如果没有 存在)或截断(如果存在)。

'wx' - 类似于 'w' 但如果路径存在则失败。

所以我们所要做的就是将wx 添加到fs.open 调用中。但是,嘿,我们不喜欢fopen-like IO。让我们继续阅读fs.writeFile

fs.readFile(filename[, options], callback)#

文件名字符串

选项对象

编码字符串 |空默认 = 空

标志字符串默认 = 'r'

回调函数

options.flag 看起来很有希望。所以我们尝试

fs.writeFile(path, data, { flag: 'wx' }, function (err) {
    if (err) throw err;
    console.log("It's saved!");
});

它非常适合单次写入。我想如果你尝试用它来解决你的任务,这段代码会以一些更奇怪的方式失败。您有一个自动“检查a_#.jpg 是否存在,如果它是空的就写在那里”操作,但是所有其他fs 状态都没有被锁定,并且a_1.jpg 文件可能会在您已经检查a_5.jpg 时自发消失.大多数* 文件系统都不是ACID 数据库,而且您至少能够执行一些原子操作这一事实是奇迹。 wx 代码很可能无法在某些平台上运行。所以为了你的理智,使用数据库,最后

更多关于苦难的信息

假设我们正在编写类似memoize-fs 的东西,它会将函数调用的结果缓存到文件系统以节省我们一些网络/cpu 时间。如果文件存在,我们可以打开文件进行读取,如果不存在,我们可以在一次调用中打开文件吗?让我们来看看这些旗帜。经过一段时间的心理练习,我们可以看到a+ 做了我们想要的:如果文件不存在,它会创建一个并打开它以进行读写,如果文件存在,它会在不清除文件的情况下这样做(就像w+ 那样)。但是现在我们既不能在(smth)File 中使用它,也不能在create(Smth)Stream 函数中使用它。这似乎是一个缺失的功能。

因此,请随意将其作为功能请求(甚至是错误)提交给 Node.js github,因为缺少原子异步文件系统 API 是 Node.js 的一个缺点。尽管don't expect 很快就会改变。

编辑。我想链接到 LinusDan Luu 的文章,说明为什么你不想对你的 fs 电话做任何聪明的事情,因为该声明大多不是基于任何东西。

【讨论】:

    【解决方案2】:

    使用a 选项怎么样?

    根据the docs

    'a+' - 打开文件进行读取和附加。如果文件不存在,则创建该文件。

    它似乎与createWriteStream完美配合

    【讨论】:

    • 即使文件存在,这也会打开文件,不幸的是,这不是我想要的。
    【解决方案3】:

    不再推荐这种方法。 fs.exists 已弃用。见 cmets。

    这里有一些选项:

    1) 有 2 个“fs”调用。第一个是“fs.exists”调用,第二个是“fs.write / read, etc”

    //checks if the file exists. 
    //If it does, it just calls back.
    //If it doesn't, then the file is created.
    function checkForFile(fileName,callback)
    {
        fs.exists(fileName, function (exists) {
            if(exists)
            {
                callback();
            }else
            {
                fs.writeFile(fileName, {flag: 'wx'}, function (err, data) 
                { 
                    callback();
                })
            }
        });
    }
    
    function writeToFile()
    {
        checkForFile("file.dat",function()
        {
           //It is now safe to write/read to file.dat
           fs.readFile("file.dat", function (err,data) 
           {
              //do stuff
           });
        });
    }
    

    2) 或者先创建一个空文件

    --- 同步:

    //If you want to force the file to be empty then you want to use the 'w' flag:
    
    var fd = fs.openSync(filepath, 'w');
    
    //That will truncate the file if it exists and create it if it doesn't.
    
    //Wrap it in an fs.closeSync call if you don't need the file descriptor it returns.
    
    fs.closeSync(fs.openSync(filepath, 'w'));
    

    --- 异步:

    var fs = require("fs");
    fs.open(path, "wx", function (err, fd) {
        // handle error
        fs.close(fd, function (err) {
            // handle error
        });
    });
    

    3) 或者使用“touch”:https://github.com/isaacs/node-touch

    【讨论】:

    • 注意 fs.exists 已被弃用,不再推荐使用。 来自Node API检查文件是否存在在打开它之前,它是一种反模式,使您容易受到竞争条件的影响:另一个进程可能会在调用 fs.exists() 和 fs.open() 之间删除文件。只需打开文件并在错误不存在时处理它。 fs.exists() 将被弃用。
    【解决方案4】:

    在单个系统调用中执行此操作,您可以使用 fs-extra npm 模块。 在此之后,将创建文件以及要放置的目录。

    const fs = require('fs-extra');
    const file = '/tmp/this/path/does/not/exist/file.txt'
    fs.ensureFile(file, err => {
        console.log(err) // => null
    });
    

    另一种方法是使用ensureFileSync,它会做同样的事情但同步。

    const fs = require('fs-extra');
    const file = '/tmp/this/path/does/not/exist/file.txt'
    fs.ensureFileSync(file)
    

    【讨论】:

    • 不幸的是,这似乎并没有告诉我文件是否存在或由操作创建 - 它只是确保它在操作后存在,所以我不知道我是否应该知道写入它。
    • fs-extra ensureFileSync 功能为我解决了问题。现在不用自己写逻辑了。
    【解决方案5】:

    使用 async / await 和 Typescript 我会这样做:

    import * as fs from 'fs'
    
    async function upsertFile(name: string) {
      try {
        // try to read file
        await fs.promises.readFile(name)
      } catch (error) {
        // create empty file, because it wasn't found
        await fs.promises.writeFile(name, '')
      }
    }
    

    【讨论】:

    • readFile 会抛出许多不同的原因,而不仅仅是丢失的文件。你应该测试error.code === 'ENOENT'
    • 除了@UpTheCreek 的观点之外,这很容易受到竞争条件的影响,而且如果整个文件已经存在,则会不必要地读取它。
    【解决方案6】:

    你可以这样做:

    function writeFile(i){
        var i = i || 0;
        var fileName = 'a_' + i + '.jpg';
        fs.exists(fileName, function (exists) {
            if(exists){
                writeFile(++i);
            } else {
                fs.writeFile(fileName);
            }
        });
    }
    

    【讨论】:

    • 这和我做过的类似;我希望有一种方法可以在单个 fs 调用中创建文件并检查是否存在。
    • 这个答案是错误的。该文件可以在调用fs.existsfs.writeFile 之间创建。
    • 这里要注意,@ebohlman 的注释不是必须的,因为fs.exists 已经是异步的,会重置调用堆栈。
    • @Steve 文件应该用wx rights 打开。所以电话看起来像fs.writeFile('path', {flag: 'wx'}, function (err, data) { ... })
    • 如果需要打开文件进行读取(如果文件存在),则需要更复杂的解决方案(即缓存):ax+
    猜你喜欢
    • 1970-01-01
    • 2012-06-14
    • 1970-01-01
    • 2014-05-12
    • 2018-04-03
    • 2016-09-05
    • 2014-03-11
    • 2011-04-03
    相关资源
    最近更新 更多