简体   繁体   English

仅当 Node.js 中不存在文件时才创建文件

[英]Creating a file only if it doesn't exist in Node.js

We have a buffer we'd like to write to a file.我们有一个要写入文件的缓冲区。 If the file already exists, we need to increment an index on it, and try again.如果文件已经存在,我们需要增加一个索引,然后再试一次。 Is there a way to create a file only if it doesn't exist, or should I just stat files until I get an error to find one that doesn't exist already?有没有办法只在文件不存在的情况下创建文件,或者我应该只统计文件,直到出现错误以找到一个不存在的文件?

For example, I have files a_1.jpg and a_2.jpg .例如,我有文件a_1.jpga_2.jpg I'd like my method to try creating a_1.jpg and a_2.jpg , and fail, and finally successfully create a_3.jpg .我希望我的方法尝试创建a_1.jpga_2.jpg并失败,最后成功创建a_3.jpg

The ideal method would look something like this:理想的方法如下所示:

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

or like this:或像这样:

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

Does anything like this exist in node's fs library? node的fs库中是否存在类似的东西?

EDIT : My question isn't if there's a separate function that checks for existence.编辑:我的问题不是是否有单独的 function 来检查是否存在。 It's this: is there a way to create a file if it doesn't exist, in a single file system call?就是这样:如果文件不存在,有没有办法在单个文件系统调用中创建文件?

As your intuition correctly guessed, the naive solution with a pair of exists / writeFile calls is wrong.正如您的直觉正确猜到的那样,带有一对exists / writeFile调用的天真解决方案是错误的。 Asynchronous code runs in unpredictable ways.异步代码以不可预测的方式运行。 And in given case it is在给定的情况下,它是

  • Is there a file a.txt ?有文件a.txt吗? — No. - 不。
  • (File a.txt gets created by another program) (文件a.txt由另一个程序创建)
  • Write to a.txt if it's possible.如果可能,写入a.txt — Okay. - 好的。

But yes, we can do that in a single call.但是,是的,我们可以在一次调用中做到这一点。 We're working with file system so it's a good idea to read developer manual on fs .我们正在使用文件系统,因此阅读fs上的开发人员手册是个好主意。 And hey, here's an interesting part.嘿, 这是一个有趣的部分。

'w' - Open file for writing. 'w' - 打开文件进行写入。 The file is created (if it does not exist) or truncated (if it exists).该文件被创建(如果它不存在)或被截断(如果它存在)。

'wx' - Like 'w' but fails if path exists. 'wx' - 类似于 'w' 但如果路径存在则失败。

So all we have to do is just add wx to the fs.open call.所以我们所要做的就是将wx添加到fs.open调用中。 But hey, we don't like fopen -like IO.但是,嘿,我们不喜欢fopen类的 IO。 Let's read on fs.writeFile a bit more.让我们多读一点fs.writeFile

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

filename String文件名字符串

options Object选项对象

encoding String |编码字符串 | Null default = null空默认值 = 空

flag String default = 'r'标志字符串默认 = 'r'

callback Function回调函数

That options.flag looks promising. options.flag看起来很有希望。 So we try所以我们尝试

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

And it works perfectly for a single write.它非常适合单次写入。 I guess this code will fail in some more bizarre ways yet if you try to solve your task with it.我猜如果你尝试用它来解决你的任务,这段代码会以一些更奇怪的方式失败。 You have an atomary "check for a_#.jpg existence, and write there if it's empty" operation, but all the other fs state is not locked, and a_1.jpg file may spontaneously disappear while you're already checking a_5.jpg .您有一个原子“检查a_#.jpg是否存在,如果为空则写入”操作,但所有其他fs状态都没有锁定,并且a_1.jpg文件可能会在您已经检查a_5.jpg自发消失。 Most * file systems are no ACID databases, and the fact that you're able to do at least some atomic operations is miraculous.大多数*文件系统都不是ACID数据库,而且您至少能够执行一些原子操作的事实是奇迹。 It's very likely that wx code won't work on some platform. wx代码很可能在某些平台上不起作用。 So for the sake of your sanity, use database, finally .因此,为了您的理智,请使用数据库,最后

Some more info for the suffering有关苦难的更多信息

Imagine we're writing something like memoize-fs that caches results of function calls to the file system to save us some network/cpu time.想象一下,我们正在编写类似memoize-fs东西,它将函数调用的结果缓存到文件系统,以节省我们一些网络/cpu 时间。 Could we open the file for reading if it exists, and for writing if it doesn't, all in the single call?如果文件存在,我们可以打开文件进行读取,如果不存在则进行写入,所有这些都可以在一次调用中完成吗? Let's take a funny look on those flags.让我们来有趣的看看这些标志。 After a while of mental exercises we can see that a+ does what we want: if the file doesn't exist, it creates one and opens it both for reading and writing, and if the file exists it does so without clearing the file (as w+ would).经过一段时间的心理练习,我们可以看到a+做了我们想要的:如果文件不存在,它会创建一个并打开它进行读写,如果文件存在,它不会清除文件(如w+会)。 But now we cannot use it neither in (smth)File , nor in create(Smth)Stream functions.但是现在我们既不能在(smth)File使用它,也不能在create(Smth)Stream函数中使用它。 And that seems like a missing feature.这似乎是一个缺失的功能。

So feel free to file it as a feature request (or even a bug) to Node.js github, as lack of atomic asynchronous file system API is a drawback of Node.因此,请随意将其作为功能请求(甚至是错误)提交给 Node.js github,因为缺乏原子异步文件系统 API 是 Node.js 的一个缺点。 Though don't expect changes any time soon.虽然不要指望很快会有变化。

Edit.编辑。 I would like to link to articles by Linus and by Dan Luu on why exactly you don't want to do anything smart with your fs calls, because the claim was left mostly not based on anything.我想链接到LinusDan Luu 的文章,这些文章讨论了为什么您不想对fs调用做任何聪明的事情,因为该声明大多没有基于任何内容。

What about using the a option?使用a选项怎么样?

According to the docs :根据文档

'a+' - Open file for reading and appending. 'a+' - 打开文件进行读取和追加。 The file is created if it does not exist.如果文件不存在,则创建该文件。

It seems to work perfectly with createWriteStream它似乎与createWriteStream完美配合

This method is no longer recommended.不再推荐此方法。 fs.exists is deprecated. fs.exists 已弃用。 See comments.看评论。

Here are some options:以下是一些选项:

1) Have 2 "fs" calls . 1) 有2 个“fs”调用 The first one is the " fs.exists " call, and the second is " fs.write / read, etc"第一个是“ 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) Or Create an empty file first: 2)或者先创建一个空文件

--- Sync: - - 同步:

//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'));

--- ASync: ---异步:

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

3) Or use " touch ": https://github.com/isaacs/node-touch 3)或使用“触摸”: https : //github.com/isaacs/node-touch

Todo this in a single system call you can use the fs-extra npm module.要在单个系统调用中完成此操作,您可以使用fs-extra npm 模块。 After this the file will have been created as well as the directory it is to be placed in.在此之后,将创建文件以及放置它的目录。

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

Another way is to use ensureFileSync which will do the same thing but synchronous.另一种方法是使用ensureFileSync它将做同样的事情,但同步。

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

With async / await and Typescript I would do:使用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, '')
  }
}

Here's a synchronous way of doing it:这是一种同步的方式:

try {
    await fs.truncateSync(filepath, 0);
} catch (err) {
    await fs.writeFileSync(filepath, "", { flag: "wx" });
}

If the file exists it will get truncated, otherwise it gets created if an error is raised.如果文件存在,它将被截断,否则如果引发错误,它将被创建。

You can do something like this:你可以这样做:

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);
        }
    });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM