96

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

例如,我有文件a_1.jpga_2.jpg. 我希望我的方法尝试创建a_1.jpgand a_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 });

节点fs库中是否存在类似的东西?

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

4

6 回答 6

88

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

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

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

'w' - 打开文件进行写入。该文件被创建(如果它不存在)或被截断(如果它存在)。

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

所以我们所要做的就是添加wxfs.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 的一个缺点。虽然不要指望很快就会发生变化。

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

于 2015-08-02T22:36:24.093 回答
32

使用该a选项怎么样?

根据文档

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

它似乎与createWriteStream

于 2017-06-28T14:31:23.507 回答
11

不再推荐这种方法。fs.exists 已弃用。看评论。

以下是一些选项:

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

//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)或使用“触摸”:https ://github.com/isaacs/node-touch

于 2015-03-12T17:19:22.840 回答
6

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

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)
于 2017-03-28T01:19:04.473 回答
2

使用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, '')
  }
}
于 2021-01-14T13:12:24.413 回答
-1

你可以这样做:

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);
        }
    });
}
于 2012-10-15T18:36:41.030 回答