115

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. I'd like my method to try creating a_1.jpg and a_2.jpg, and fail, and finally successfully create 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?

EDIT: My question isn't if there's a separate function that checks for existence. It's this: is there a way to create a file if it doesn't exist, in a single file system call?

configurator
  • 40,828
  • 14
  • 81
  • 115
  • Possible duplicate of [create an empty file in nodejs?](http://stackoverflow.com/questions/12809068/create-an-empty-file-in-nodejs) – velop Mar 15 '16 at 09:11
  • Does this answer your question? [Create a file if it doesn't already exist](https://stackoverflow.com/questions/31195391/create-a-file-if-it-doesnt-already-exist) – clickbait Apr 07 '22 at 13:00

8 Answers8

102

As your intuition correctly guessed, the naive solution with a pair of exists / writeFile calls is wrong. Asynchronous code runs in unpredictable ways. And in given case it is

  • Is there a file a.txt? — No.
  • (File a.txt gets created by another program)
  • Write to a.txt if it's possible. — 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. And hey, here's an interesting part.

'w' - Open file for writing. The file is created (if it does not exist) or truncated (if it exists).

'wx' - Like 'w' but fails if path exists.

So all we have to do is just add wx to the fs.open call. But hey, we don't like fopen-like IO. Let's read on fs.writeFile a bit more.

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

filename String

options Object

encoding String | Null default = null

flag String default = 'r'

callback Function

That options.flag looks promising. 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. Most* file systems are no ACID databases, and the fact that you're able to do at least some atomic operations is miraculous. It's very likely that wx code won't work on some platform. 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. 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). But now we cannot use it neither in (smth)File, nor in create(Smth)Stream functions. 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. 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.

polkovnikov.ph
  • 6,256
  • 6
  • 44
  • 79
39

What about using the a option?

According to the docs:

'a+' - Open file for reading and appending. The file is created if it does not exist.

It seems to work perfectly with createWriteStream

Alvaro
  • 40,778
  • 30
  • 164
  • 336
12

This method is no longer recommended. fs.exists is deprecated. See comments.

Here are some options:

1) Have 2 "fs" calls. The first one is the "fs.exists" call, and the second is "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) Or Create an empty file first:

--- 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

Katie
  • 45,622
  • 19
  • 93
  • 125
  • 12
    **Note that fs.exists is deprecated and no longer recommended for use.** **From the [Node API](https://nodejs.org/api/fs.html#fs_fs_exists_path_callback)**: _checking if a file exists before opening it is an anti-pattern that leaves you vulnerable to race conditions: another process may remove the file between the calls to fs.exists() and fs.open(). Just open the file and handle the error when it's not there. fs.exists() will be deprecated._ – Andrew Faulkner Aug 20 '15 at 06:17
7

Todo this in a single system call you can use the fs-extra npm module. 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.

const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFileSync(file)
Alexis Tyler
  • 1,394
  • 6
  • 30
  • 48
Kumar Vaibhav
  • 2,632
  • 8
  • 32
  • 54
  • 2
    Unfortunately this doesn't seem to tell me if the file existed or was created by the operation - it just makes sure it exists after the operation, so I can't know if I should know write to it. – configurator Mar 28 '17 at 08:21
  • The fs-extra ensureFileSync function did the trick for me. Now I don't have to write the logic myself. – Daniel Eagle Nov 05 '19 at 22:09
3

With async / await and Typescript I would do:

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, '')
  }
}
Florian Ludewig
  • 4,338
  • 11
  • 71
  • 137
  • 3
    readFile will throw for a number of different reasons, not just a missing file. You should test `error.code === 'ENOENT'`. – UpTheCreek Apr 28 '21 at 08:00
  • In addition to @UpTheCreek's point, this is vulnerable to race conditions, plus unnecessarily reads the entire file if it does already exist. – Jolly Roger Jan 19 '22 at 16:39
2

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.

Benji
  • 310
  • 3
  • 12
0

This works for me.

// Use the file system fs promises 
const {access} = require('fs/promises');

// File Exist returns true
// dont use exists which is no more!
const fexists =async (path)=> {  
    try {
      await access(path);
      return true;
    } catch {
      return false;
    }
  }


// Wrapper for your main program
  async function mainapp(){
  if( await fexists("./users.json")){
    console.log("File is here");
  } else { 
    console.log("File not here -so make one");
  }
}

// run your program
mainapp();

    

Just keep eye on your async - awaits so everthing plays nice. hope this helps.

  • 1
    Your answer could be improved by adding more information on what the code does and how it helps the OP. – Tyler2P Oct 27 '22 at 14:55
-1

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);
        }
    });
}
Ivan Lazarevic
  • 371
  • 2
  • 7
  • 1
    This is similar to what I've done; I was hoping there was a way to create the file and check for existence in a single fs call. – configurator Oct 16 '12 at 16:19
  • 6
    This answer is just wrong. The file could be created in-between the calls to `fs.exists` and `fs.writeFile`. – polkovnikov.ph Jan 26 '15 at 16:39
  • @ebohlman Can you explain please? Here is the question: http://stackoverflow.com/questions/28349548/how-does-process-nexttick-keep-my-stack-from-blowing-up – borisdiakur Feb 05 '15 at 16:50
  • 2
    Just to note here, @ebohlman's comment is not necessary, because `fs.exists` is already async and will reset the call stack. – loganfsmyth Feb 05 '15 at 18:32
  • 3
    @Steve File should be opened with [`wx` rights](http://nodejs.org/api/fs.html#fs_fs_open_path_flags_mode_callback). So the call looks like `fs.writeFile('path', {flag: 'wx'}, function (err, data) { ... })`. – polkovnikov.ph Feb 26 '15 at 09:59
  • 1
    A more complicated solution is needed if one needs to open file for reading if it exists, and open for writing if it doesn't (i.e. caching): `ax+`. – polkovnikov.ph Feb 26 '15 at 10:03
  • @polkovnikov.ph could you please post this as an answer? It deserves more attention since it is obviously the better solution. – Josef Engelfrost Jul 30 '15 at 10:22
  • @JosefEngelfrost Done. – polkovnikov.ph Aug 02 '15 at 22:36