How to write file if parent folder doesn't exist? How to write file if parent folder doesn't exist? node.js node.js

How to write file if parent folder doesn't exist?


As of Node v10, this is built into the fs.mkdir function, which we can use in combination with path.dirname:

var fs = require('fs');var getDirName = require('path').dirname;function writeFile(path, contents, cb) {  fs.mkdir(getDirName(path), { recursive: true}, function (err) {    if (err) return cb(err);    fs.writeFile(path, contents, cb);  });}

For older versions, you can use mkdirp:

var mkdirp = require('mkdirp');var fs = require('fs');var getDirName = require('path').dirname;function writeFile(path, contents, cb) {  mkdirp(getDirName(path), function (err) {    if (err) return cb(err);        fs.writeFile(path, contents, cb);  });}

If the whole path already exists, mkdirp is a noop. Otherwise it creates all missing directories for you.

This module does what you want: https://npmjs.org/package/writefile . Got it when googling for "writefile mkdirp". This module returns a promise instead of taking a callback, so be sure to read some introduction to promises first. It might actually complicate things for you.

The function I gave works in any case.


I find that the easiest way to do this is to use the outputFile() method from the fs-extra module.

Almost the same as writeFile (i.e. it overwrites), except that if the parent directory does not exist, it's created. options are what you'd pass to fs.writeFile().

Example:

var fs = require('fs-extra');var file = '/tmp/this/path/does/not/exist/file.txt'fs.outputFile(file, 'hello!', function (err) {    console.log(err); // => null    fs.readFile(file, 'utf8', function (err, data) {        console.log(data); // => hello!    });});

It also has promise support out of the box these days!.


Edit

NodeJS version 10.12.0 has added a native support for both mkdir and mkdirSync to create the parent director recursively with recursive: true option as the following:

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

And if you prefer fs Promises API, you can write

fs.promises.mkdir(targetDir, { recursive: true });

Original Answer

Create the parent directories recursively if they do not exist! (Zero dependencies)

const fs = require('fs');const path = require('path');function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {  const sep = path.sep;  const initDir = path.isAbsolute(targetDir) ? sep : '';  const baseDir = isRelativeToScript ? __dirname : '.';  return targetDir.split(sep).reduce((parentDir, childDir) => {    const curDir = path.resolve(baseDir, parentDir, childDir);    try {      fs.mkdirSync(curDir);    } catch (err) {      if (err.code === 'EEXIST') { // curDir already exists!        return curDir;      }      // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.      if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure.        throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);      }      const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;      if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) {        throw err; // Throw if it's just the last created dir.      }    }    return curDir;  }, initDir);}

Usage

// Default, make directories relative to current working directory.mkDirByPathSync('path/to/dir');// Make directories relative to the current script.mkDirByPathSync('path/to/dir', {isRelativeToScript: true});// Make directories with an absolute path.mkDirByPathSync('/path/to/dir');

Demo

Try It!

Explanations

  • [UPDATE] This solution handles platform-specific errors like EISDIR for Mac and EPERM and EACCES for Windows.
  • This solution handles both relative and absolute paths.
  • In the case of relative paths, target directories will be created (resolved) in the current working directory. To Resolve them relative to the current script dir, pass {isRelativeToScript: true}.
  • Using path.sep and path.resolve(), not just / concatenation, to avoid cross-platform issues.
  • Using fs.mkdirSync and handling the error with try/catch if thrown to handle race conditions: another process may add the file between the calls to fs.existsSync() and fs.mkdirSync() and causes an exception.
    • The other way to achieve that could be checking if a file exists then creating it, I.e, if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. But this is an anti-pattern that leaves the code vulnerable to race conditions.
  • Requires Node v6 and newer to support destructuring. (If you have problems implementing this solution with old Node versions, just leave me a comment)