nornagon
nornagon

Reputation: 15821

nodejs - Temporary file name

In node.js, how do I generate a unique temporary file name, a la mkstemp(3)? I want to atomically write a file using fs.rename.

Upvotes: 80

Views: 118250

Answers (9)

Benny Code
Benny Code

Reputation: 54812

I am using Node.js v20.17.0 and utilizing crypto.randomUUID to generate unique filenames and fs.mkdtempSync to create temporary directories within the OS's temp directory (typically /tmp on Linux) with a unique prefix.

Additionally, I provide cleanup options to ensure that the temporary file and directory are deleted when the program exits successfully (exit), when the user exits (SIGINT via Ctrl+C), or when the process manager requests a graceful shutdown (SIGTERM).

Code:

import crypto from 'crypto';
import fs from 'fs';
import os from 'os';
import path from 'path';

async function createTempFile(content: string = '', cleanup: boolean = true) {
  const uuid = crypto.randomUUID();
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${uuid}-`));
  const tempFileName = path.join(tempDir, `${uuid}.txt`);

  fs.writeFileSync(tempFileName, content);
  console.debug(`Temporary file created at: ${tempFileName}`);

  if (cleanup) {
    const deleteFile = () => {
      fs.rmSync(tempDir, { force: true, recursive: true });
    };

    // Delete temporary file when program exits
    process.once('exit', deleteFile);
    process.once('SIGINT', deleteFile);
    process.once('SIGTERM', deleteFile);
  }

  return tempFileName;
}

createTempFile();
createTempFile('This file will be deleted.', true);
createTempFile('This file will remain.', false);

Upvotes: 2

Jefferey Cave
Jefferey Cave

Reputation: 2889

Rather than a temporary file, it may be feasible to create a temporary directory using the built in libraries.

https://nodejs.org/api/fs.html#fspromisesmkdtempprefix-options

import fs from 'node:fs/promises';

let folder = await fs.mkdtemp('foo-');

Knowing that the location you are working in, you can use constant names inside.

Upvotes: 12

Ohad Cohen
Ohad Cohen

Reputation: 6144

use npm package tempfile

import tempfile from 'tempfile';

Or

let tempfile = "";
import('tempfile').then(a => {
    tempfile = a.default;
});

and then:

let filename = tempfile() //e,g: /tmp/5fc8a50f-e9cd-4420-b62a-e365b3ef2c2a
let imagefilename = tempfile(".jpeg") // e,g: /tmp/5a6dea08-a704-4fa8-9d0a-8ffa5e134543.jpeg

Upvotes: -1

Erik Aronesty
Erik Aronesty

Reputation: 12897

Temp file creation in node and python suffer from race conditions involving permission changes, and cross-platform issues, especially on Windows where it's challenging to test ACL's.

The result is that it can literally hang your machine asking for a temp file in many modern languages. (If node doesn't have access to the temp dir, depending on the ACLs, it can get EEXIST, even when the file doesn't exist - the result of which can be an infinite loop.).

The simplest solution is to use enough entropy that the likelihood of a collision is negligible (in the cryptographic sense).

This also has the side effect of making temp file creation secure across all platforms, without needing to heavily vet the source code. As long as your random numbers are secure, there is no way to predict the file that will be used to usurp permissions or access.

And it helps with programs that need to pass file names around, not file handles.

const crypto = require('crypto');
const os = require('os'); 
const path = require('path'); 

function tmpFile(prefix, suffix, tmpdir) {
    prefix = (typeof prefix !== 'undefined') ? prefix : 'tmp.';
    suffix = (typeof suffix !== 'undefined') ? suffix : '';
    tmpdir = tmpdir ? tmpdir : os.tmpdir();
    return path.join(tmpdir, prefix + crypto.randomBytes(16).toString('hex') + suffix);
}

The only disadvantage is that this will not work on FAT formatted partitions, which can still be common on USB sticks.

Upvotes: 14

mpen
mpen

Reputation: 282885

Similar to kinematic's answer, but with 2 bytes extra entropy and letters instead of numbers:

import Crypto from 'crypto';
import {tmpdir} from 'os';
import Path from 'path';

function tmpFile(ext) {
    return Path.join(tmpdir(),`archive.${Crypto.randomBytes(6).readUIntLE(0,6).toString(36)}.${ext}`);
}

Usage:

const file = tmpFile('tar.gz'); // "/tmp/archive.1scpz5ew5d.tar.gz"

I'm creating archives, so I chose "archive" as the basename, but you can change it as you see fit.

Upvotes: 6

Fernando Carvajal
Fernando Carvajal

Reputation: 1945

Try this function, secure and without vulnerabilities. NODE 8.x LTS

function tempFile (name = 'temp_file', data = '', encoding = 'utf8') {
    const fs = require('fs');
    const os = require('os');
    const path = require('path');

    return new Promise((resolve, reject) => {
        const tempPath = path.join(os.tmpdir(), 'foobar-');
        fs.mkdtemp(tempPath, (err, folder) => {
            if (err) 
                return reject(err)

            const file_name = path.join(folder, name);

            fs.writeFile(file_name, data, encoding, error_file => {
                if (error_file) 
                    return reject(error_file);

                resolve(file_name)
            })
        })
    })
}

It resolves the PATH of the temp file, rejects mkdtemp or writeFile errors

// C:\Users\MYPC\AppData\Local\Temp\foobar-3HmKod\temp_file
// /temp/Temp/foobar-3HmKod/temp_file
tempFile().then(path => console.log(path)).catch(e => console.log("error", e)) //or

// C:\Users\MYPC\AppData\Local\Temp\foobar-9KHuxg\hola.txt
// /temp/Temp/foobar-9KHuxg/hola.txt
tempFile('hola.txt', 'hello there').then(path => console.log(path)).catch(e => console.log("e", e))

Upvotes: 9

Steve Jansen
Steve Jansen

Reputation: 9494

Another popular package is tmp.

Upvotes: 57

Marc
Marc

Reputation: 1976

Maybe you already found node-temp in the meantime.

Upvotes: 29

kinematic
kinematic

Reputation: 463

Without using any additional plugins:

var crypto = require('crypto');
var fs = require('fs'); 

var filename = 'foo'+crypto.randomBytes(4).readUInt32LE(0)+'bar';
fs.writeFileSync(filename, 'baz');

EDIT: read comments.

Upvotes: 25

Related Questions