Create Directory When Writing To File In Node.js
Javascriptnode.jsJavascript Problem Overview
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?
Javascript Solutions
Solution 1 - Javascript
##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);
##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.
Solution 2 - Javascript
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);
}
Solution 3 - Javascript
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.
Solution 4 - Javascript
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
Solution 5 - Javascript
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);
}
Solution 6 - Javascript
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);
});
};
Solution 7 - Javascript
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);
})();
Solution 8 - Javascript
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)