49

I have a need a create a temporary "scratch" directory on-demand in node.js. The requirements are:

  • the dirname should be randomized (i.e. /tmp/aDIge4G/
  • the directory will be created within /tmp which may already have other randomly named directories.
  • if the directory already exists, I should throw rather than use it and overwrite someone else's work
  • this needs to be safe in a concurrent environment. I can't just check if the directory exists and then create it if it doesn't because someone else may have created a directory with the same name after I checked.

In other words, I need the answer to this question but for directories, not files.

This answer says that what I want to do can be accomplished by mkdir -p, but Node doesn't have the -p flag for fs.mkdir

Community
  • 1
  • 1
BonsaiOak
  • 27,741
  • 7
  • 30
  • 54

6 Answers6

68

The current node api propose to create a temporary folder : https://nodejs.org/api/fs.html#fs_fs_mkdtemp_prefix_options_callback

which gives :

fs.mkdtemp(path.join(os.tmpdir(), 'foo-'), (err, folder) => {
  if (err) throw err;
  console.log(folder);
  // Prints: /tmp/foo-itXde2
});
// folder /tmp/foo-itXde2 has been created on the file system
Hettomei
  • 1,934
  • 1
  • 16
  • 14
19

You can try package "tmp". It has a configuration parameter "template" which in turn uses Linux's mkstemp function which probably solves all your requirements.

Pavol Pitonak
  • 509
  • 1
  • 5
  • 15
  • 12
    Should I install another package in my repository which already has 16657 dependencies?... Where is it going? – Marecky Apr 28 '21 at 09:45
5

A simple way to create unique directories would be using universally unique identifiers (UUIDs) within the path name.

Here is an example using pure-uuid:

const fs = require('fs-extra');
const path = require('path');
const UUID = require('pure-uuid');

const id = new UUID(4).format();
const directory = path.join('.', 'temp', id);

fs.mkdirs(directory).then(() => {
  console.log(`Created directory: ${directory}`);
});

You will get an output like this:

Created directory: temp\165df8b8-18cd-4151-84ca-d763e2301e14

Note: In the code above I am using fs-extra as a drop-in replacement for fs, so you don't have to care about mkdir -p because fs-extra will create the directory and any necessary subdirectories.

Tip: If you want to save your directories within the operation system's default temp directory, then you can make use of os.tmpdir(). Here's an example of how this works:

const fs = require('fs-extra');
const os = require('os');
const path = require('path');
const UUID = require('pure-uuid');

const id = new UUID(4).format();
const directory = path.join(os.tmpdir(), id);

fs.mkdirs(directory).then(() => {
  console.log(`Created directory: ${directory}`);
});

Created directory: C:\Users\bennyn\AppData\Local\Temp\057a9978-4fd7-43d9-b5ea-db169f222dba

Benny Code
  • 51,456
  • 28
  • 233
  • 198
0

Use fs.Stats to check if it exists?

Something like this?

fs.stat(path, function(err, stats) {

    if (!stats.isDirectory()) {
        // create directory here
    }
}
Matthew Bakaitis
  • 11,600
  • 7
  • 43
  • 53
0

You can use tmpDir method of fs-jetpack library. It uses just a crypto.randomBytes underneath and generates 128-bits-of-entropy random string as a directory name. If you create 2^32 (4 billions) temp-dirs, the statistical name collision chance is still 1 to 2^96, what is in the same realms of possibility as me being hit by a meteorite this second (somehow I live with this fear ;).

Relying on probabilities of big numbers (BTW, that's what git-hashes do) is much better and simpler than trying to figure out some clever way of omitting name collisions in concurrent environments.

Example code:

const jetpack = require("fs-jetpack");

const tmp = jetpack.tmpDir();

// Do some stuff in the directory...
tmp.write("some_stuff.txt", "Hi!");

// After you're done with your directory easily remove it.
tmp.remove();
0

Another way

const {exec} = require('child_process')
const {promisify} = require('util')

promisify(exec)('mktemp -d')
  .then(({stdout: tempDirName}) => console.log(tempDirName))
Sang
  • 4,049
  • 3
  • 37
  • 47