Reputation: 1612
I am currently trying to figure out the "correct" way to check if a local folder exists, before saving a file into it and am a little bit confused by the nodejs docs.
fs.exists() is deprecated and you should use fs.stat() or fs.access(). So far so good.
fs.stat():
Using fs.stat() to check for the existence of a file before calling fs.open(), fs.readFile() or fs.writeFile() is not recommended. Instead, user code should open/read/write the file directly and handle the error raised if the file is not available.
This states, that I should just try to write, catch the error, create the folder, and try again. Also fine by me, even though I am moving/renaming the file , so I don't directly use one of the three mentioned functions.
then, the doc says:
To check if a file exists without manipulating it afterwards, fs.access() is recommended.
Since I am not really modifying the file, but "only" it's content, one might argue that this is the way to go.
But then again, the fs.access() documentation goes into detail why that's a bad idea, too:
Using fs.access() to check for the accessibility of a file before calling fs.open(), fs.readFile() or fs.writeFile() is not recommended. Doing so introduces a race condition, since other processes may change the file's state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file is not accessible.
Yadda yadda yadda, there are already somewhat related questions (here, or here), but is there any official info on the "best practice" that's not more than two years old?
Upvotes: 29
Views: 28507
Reputation: 3261
You can easily do it synchronously via existsSync()
.
But if you want to do it asynchronously, just do it like this:
await fs.promises.access("path");
Or put it within a try-catch like this...
try {
await fs.promises.access("path");
// The check succeeded
} catch (error) {
// The check failed
}
Upvotes: 20
Reputation: 3681
I guess the documentation suggest to use fs.access
to check if a file exists when there's no manipulation afterwards is because it's a lower level and simple API with a single purpose of checking file-access metadata (which include whether a file exists or not). A simpler API might be little bit better too in term of performance as it may just be a straightforward mapping to the underlying native code.(NOTE: I haven't do any benchmark, take my statement with grain of salt).
fs.stat
provide a higher level of abstraction compared to fs.access
. It returns information beyond file-access.
import {promises as fs} from "fs";
import * as oldfs from "fs";
(async function() {
// fs.stat has higher level abstraction
const stat = await fs.stat(__dirname);
console.log(stat.isDirectory());
console.log(stat.isFile());
console.log(stat.ctime);
console.log(stat.size);
try {
// fs.access is low level API, it throws error when it doesn't match any of the flags
// is dir exists? writable? readable?
await fs.access(__dirname, oldfs.constants.F_OK | oldfs.constants.W_OK | oldfs.constants.R_OK);
} catch (error) {
console.log(`${__dirname} is not exists / writable / readable`);
}
})();
Upvotes: 16
Reputation: 2762
I think the doc is pretty self-explanatory.
If you just want to check whether the file exists or not - without trying to open/read/write into it afterwards, you should use access :
// Check if the file exists in the current directory, and if it is writable.
fs.access(file, fs.constants.F_OK | fs.constants.W_OK, (err) => {
if (err) {
console.error(
`${file} ${err.code === 'ENOENT' ? 'does not exist' : 'is read-only'}`);
} else {
console.log(`${file} exists, and it is writable`);
}
});
If you want to open/read/write into the file, you should not call access to check for existence, but instead directly do your operation and handle the error yourself:
//write (NOT RECOMMENDED)
fs.access('myfile', (err) => {
if (!err) {
console.error('myfile already exists');
return;
}
fs.open('myfile', 'wx', (err, fd) => {
if (err) throw err;
writeMyData(fd);
});
});
//write (RECOMMENDED)
fs.open('myfile', 'wx', (err, fd) => {
if (err) {
if (err.code === 'EEXIST') {
console.error('myfile already exists');
return;
}
throw err;
}
writeMyData(fd);
});
These examples are from the documentation that you linked.
You could also use existsSync()
which is not deprecated, if you do a simple check at start-up - avoid if you are using this in a loop or multiple times since this is blocking the nodejs event loop.
Upvotes: -2
Reputation: 74655
The proper way, although I don't think node supports it, is to open the directory to get a dirfd. That is only open should be used and then faccess or fstat can be called on the dirfd or fd. Then if open of the directory succeed then, use openat with the dirfd to open the file.
I wrote a http server that used fs.createReadStream and handled the error if the path was not a file. it also used pause and resume to be able to use fstat on the open file before the stream started to pipe to the response.
Upvotes: 0