263

I've been tinkering with Node.js and found a little problem. I've got a script which resides in a directory called data. I want the script to write some data to a file in a subdirectory within the data subdirectory. However I am getting the following error:

{ [Error: ENOENT, open 'D:\data\tmp\test.txt'] errno: 34, code: 'ENOENT', path: 'D:\\data\\tmp\\test.txt' }

The code is as follows:

var fs = require('fs');
fs.writeFile("tmp/test.txt", "Hey there!", function(err) {
    if(err) {
        console.log(err);
    } else {
        console.log("The file was saved!");
    }
}); 

Can anybody help me in finding out how to make Node.js create the directory structure if it does not exits for writing to a file?

mega6382
  • 9,211
  • 17
  • 48
  • 69
Hirvesh
  • 7,636
  • 15
  • 59
  • 72
  • 11
    `fs.promises.mkdir(path.dirname("tmp/test.txt"), {recursive: true}).then(x => fs.promises.writeFile("tmp/test.txt", "Hey there!"))` – Offenso Nov 05 '18 at 18:43

11 Answers11

294

Node > 10.12.0

fs.mkdir now accepts a { recursive: true } option like so:

// Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
});

or with a promise:

fs.promises.mkdir('/tmp/a/apple', { recursive: true }).catch(console.error);

Notes,

  1. In many case you would use fs.mkdirSync rather than fs.mkdir

  2. It is harmless / has no effect to include a trailing slash.

  3. mkdirSync/mkdir no nothing harmlessly if the directory already exists, there's no need to check for existence.

Node <= 10.11.0

You can solve this with a package like mkdirp or fs-extra. If you don't want to install a package, please see Tiago Peres França's answer below.

Fattie
  • 27,874
  • 70
  • 431
  • 719
David Weldon
  • 63,632
  • 11
  • 148
  • 146
185

If you don't want to use any additional package, you can call the following function before creating your file:

var path = require('path'),
    fs = require('fs');

function ensureDirectoryExistence(filePath) {
  var dirname = path.dirname(filePath);
  if (fs.existsSync(dirname)) {
    return true;
  }
  ensureDirectoryExistence(dirname);
  fs.mkdirSync(dirname);
}
Chris Calo
  • 7,518
  • 7
  • 48
  • 64
Tiago Peres França
  • 3,056
  • 2
  • 21
  • 23
  • 2
    This should use `statSync` instead of `existsSync`, based on http://stackoverflow.com/questions/4482686/check-synchronously-if-file-directory-exists-in-node-js/4482701#4482701 – GavinR Feb 03 '16 at 22:18
  • @GavinR You're right, fs.existsSync is deprecated. I'll update the answer. – Tiago Peres França Feb 05 '16 at 12:53
  • 1
    `path` is also a package that needs to be required just as `fs`: `var path = require('path')` in case anyone is wondering. See [node documentation](https://nodejs.org/api/path.html). – Rafael Emshoff Oct 14 '16 at 20:41
  • 13
    [`fs.existsSync` is *not* deprecated](https://nodejs.org/api/fs.html#fs_fs_existssync_path), only `fs.exists` is. – zzzzBov Nov 10 '16 at 20:09
  • @RafaelCichocki I just edited the answer to reflect your comment. Thank You for the observation. – Tiago Peres França Nov 23 '16 at 16:34
  • 7
    There's been some confusion about whether the function fs.existsSync has been deprecated or not. At first, by my understanding, I thought it was, so I updated the answer to reflect this. But now, as pointed by @zzzzBov, the documentation clearly states that only fs.exists has been deprecated, the use of fs.existsSync is still valid. For this reason I deleted the previous code and my answer now contains only the simpler solution (with the use of fs.existsSync). – Tiago Peres França Nov 23 '16 at 16:36
  • Why is the recusive call to ensureDirectoryExistence() called before the fs.mkdirSync? – chrismarx Feb 06 '18 at 23:34
  • 1
    @chrismarx imagine the following path: "/home/documents/a/b/c/myfile.txt". "/home/documents" exists, while everything in front of it doesn't. When "ensureDirectoryExistence" is called for the 1st time, dirname is "/home/documents/a/b/c". I can't call fs.mkdirSync(dirname) right now because "/home/documents/a/b" also doesn't exist. To create the directory "c", I need to first ensure the existence of "/home/documents/a/b". – Tiago Peres França Feb 08 '18 at 11:25
  • Oh, duh, yes, thank you, I knew it had to create the missing directories, wasn't seeing how the recursion worked here, thanks! – chrismarx Feb 10 '18 at 16:51
  • This almost works but does not completely work! If the argument is just a directory structure without any filename, it fails. For example, it failed for me when I passed in an argument like this: "./Test/" – Suhas Aug 14 '18 at 11:04
  • @Suhas It does work, since the question explicitly asks how to create a directory while writing a FILE. ./Test/ is not a file path. However, it is extremely simple to adapt this answer to solve your specific problem. just pass a dummy path (./Test/dummy) or edit the function itself to expect a directory. – Tiago Peres França Aug 14 '18 at 14:06
  • @Offenso what does Jesus have to do with any of this? – Mike Kormendy Jul 15 '20 at 03:41
  • Why the "ensureDirectoryExistence(dirname);" has camed before the "fs.mkdirSync(dirname);" It's something like wole loop. – Amer May 14 '21 at 04:27
  • 1
    You can also just do `fs.mkdirSync(path.dirname(filePath), { recursive: true });` . If the directory doesnt exist it will be created – Gilbert Jan 10 '22 at 12:33
81

With node-fs-extra you can do it easily.

Install it

npm install --save fs-extra

Then use the outputFile method. Its documentation says:

Almost the same as writeFile (i.e. it overwrites), except that if the parent directory does not exist, it's created.

You can use it in four ways.

Async/await

const fse = require('fs-extra');

await fse.outputFile('tmp/test.txt', 'Hey there!');

Using Promises

If you use promises, this is the code:

const fse = require('fs-extra');

fse.outputFile('tmp/test.txt', 'Hey there!')
   .then(() => {
       console.log('The file has been saved!');
   })
   .catch(err => {
       console.error(err)
   });

Callback style

const fse = require('fs-extra');

fse.outputFile('tmp/test.txt', 'Hey there!', err => {
  if(err) {
    console.log(err);
  } else {
    console.log('The file has been saved!');
  }
})

Sync version

If you want a sync version, just use this code:

const fse = require('fs-extra')

fse.outputFileSync('tmp/test.txt', 'Hey there!')

For a complete reference, check the outputFile documentation and all node-fs-extra supported methods.

lifeisfoo
  • 15,478
  • 6
  • 74
  • 115
28

Shameless plug alert!

You will have to check for each directory in the path structure you want and create it manually if it doesn't exist. All the tools to do so are already there in Node's fs module, but you can do all of that simply with my mkpath module: https://github.com/jrajav/mkpath

jonvuri
  • 5,738
  • 3
  • 24
  • 32
  • 1
    will that create the file directly or just the directory structure? I'm looking for a solution which create the file along with the directory structure when creating the file. – Hirvesh Nov 24 '12 at 15:41
  • Just the directory structure. You would the mkdir/path first and, if there weren't any errors, proceed to writing your file. It would be simple enough to write a function to do both simultaneously, given a full path to a file to write - just split off the filename using [path.basename](http://nodejs.org/api/path.html#path_path_basename_p_ext) – jonvuri Nov 24 '12 at 15:42
  • 1
    In fact, it was so simple that I [wrote it in 2 minutes](https://gist.github.com/4140206). :) (Untested) – jonvuri Nov 24 '12 at 15:46
  • Update: Tested and edited, please try it again if it didn't work the first time. – jonvuri Nov 24 '12 at 15:55
  • 8
    @Kiyura How is this different from the widely used [mkdirp](https://npmjs.org/package/mkdirp)? – David Weldon Nov 24 '12 at 18:42
  • Dammit! I gave into your `shameless` advertisement! Thanks for the help! – dthree Oct 04 '13 at 03:44
  • anyone who is interested. the module is available on npm https://www.npmjs.com/package/mkpath – Aryeh Armon Feb 14 '17 at 09:29
  • Jesus.. Use recursive: `fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))` – Offenso Nov 05 '18 at 18:45
18

Same answer as above, but with async await and ready to use!

const fs = require('fs/promises');
const path = require('path');

async function isExists(path) {
  try {
    await fs.access(path);
    return true;
  } catch {
    return false;
  }
};

async function writeFile(filePath, data) {
  try {
    const dirname = path.dirname(filePath);
    const exist = await isExists(dirname);
    if (!exist) {
      await fs.mkdir(dirname, {recursive: true});
    }
    
    await fs.writeFile(filePath, data, 'utf8');
  } catch (err) {
    throw new Error(err);
  }
}

Example:

(async () {
  const data = 'Hello, World!';
  await writeFile('dist/posts/hello-world.html', data);
})();
illvart
  • 847
  • 9
  • 10
11

Since I cannot comment yet, I'm posting an enhanced answer based on @tiago-peres-frança fantastic solution (thanks!). His code does not make directory in a case where only the last directory is missing in the path, e.g. the input is "C:/test/abc" and "C:/test" already exists. Here is a snippet that works:

function mkdirp(filepath) {
    var dirname = path.dirname(filepath);

    if (!fs.existsSync(dirname)) {
        mkdirp(dirname);
    }

    fs.mkdirSync(filepath);
}
micx
  • 111
  • 1
  • 4
  • 1
    That's because @tiago's solution expects a **file** path. In your case, `abc` is interpreted as the file you need to create a directory for. To also create the `abc` directory, add a dummy file to your path, e.g. `C:/test/abc/dummy.txt`. – Sphinxxx Dec 17 '17 at 22:11
  • Use recursive: `fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))` – Offenso Nov 05 '18 at 18:45
  • 1
    @Offenso it's the best solution, but for Node.js version 10.12 and above only. – Nickensoul Apr 30 '19 at 14:18
9

My advise is: try not to rely on dependencies when you can easily do it with few lines of codes

Here's what you're trying to achieve in 14 lines of code:

fs.isDir = function(dpath) {
    try {
        return fs.lstatSync(dpath).isDirectory();
    } catch(e) {
        return false;
    }
};
fs.mkdirp = function(dirname) {
    dirname = path.normalize(dirname).split(path.sep);
    dirname.forEach((sdir,index)=>{
        var pathInQuestion = dirname.slice(0,index+1).join(path.sep);
        if((!fs.isDir(pathInQuestion)) && pathInQuestion) fs.mkdirSync(pathInQuestion);
    });
};
Alex C.
  • 4,021
  • 4
  • 21
  • 24
  • 1
    Wouldn't the third line be better like this? `return fs.lstatSync(dpath).isDirectory()`, otherwise what would happen if isDirectory() returns false? – Giorgio Aresu Nov 17 '16 at 09:44
  • 2
    Use recursive: `fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))` – Offenso Nov 05 '18 at 18:45
  • 1
    @Offenso it's not supported by a node 8 – Ievgen Nov 18 '19 at 16:48
2

I just published this module because I needed this functionality.

https://www.npmjs.org/package/filendir

It works like a wrapper around Node.js fs methods. So you can use it exactly the same way you would with fs.writeFile and fs.writeFileSync (both async and synchronous writes)

Kev
  • 5,049
  • 5
  • 32
  • 53
1

IDK why, but this mkdir always make an extra directory with the file name if also the filename is included in the path; e.g.

// fileName = './tmp/username/some-random.jpg';
try {
  mkdirSync(fileName, { recursive: true })
} catch (error) {};

enter image description here

Solution

It can be solved this way: fileName.split('/').slice(0, -1).join('/') which only make directories up to the last dir, not the filename itself.

// fileName = './tmp/username/some-random.jpg';
try {
  mkdirSync(fileName.split('/').slice(0, -1).join('/'), { recursive: true })
} catch (error) {};

enter image description here

Mechanic
  • 5,015
  • 4
  • 15
  • 38
1

As simple as this, create recursively if does not exist:

if (!fs.existsSync(dir)) {
  fs.mkdirSync(dir, { recursive: true })
}
João Pimentel Ferreira
  • 14,289
  • 10
  • 80
  • 109
0

Here is a complete solution to create the folder with all the needed subfolders, and then writing the file, all in one function.

This is an example assuming you are creating a backup file and you want to pass the backup folder name in the function (hence the name of the variables)


  createFile(backupFolderName, fileName, fileContent) {
    const csvFileName = `${fileName}.csv`;

    const completeFilePath = join(process.cwd(), 'backups', backupFolderName);

    fs.mkdirSync(completeFilePath, { recursive: true });

    fs.writeFileSync(join(completeFilePath, csvFileName), fileContent, function (err) {
      if (err) throw err;
      console.log(csvFileName + ' file saved');
    })
  }
Fed
  • 1,696
  • 22
  • 29