fs.appendFile but fail if path does *not* exist - node.js

Looking through the fs docs, I am looking for a flag that I can use with fs.appendFile, where an error will be raised if the path does not exist.
I am seeing flags that pertain to raising errors if the path does already exist, but I am not seeing flags that will raise errors if the path does not exist -
https://nodejs.org/api/fs.html

First off, I assume you mean fs.appendFile(), since the fs.append() you refer to is not in the fs module.
There does not appear to be a flag that opens the file for appending that returns an error if the file does not exist. You could write one yourself. Here's a general idea for how to do so:
fs.appendToFileIfExist = function(file, data, encoding, callback) {
// check for optional encoding argument
if (typeof encoding === "function") {
callback = encoding;
encoding = 'utf8';
}
// r+ opens file for reading and writing. Error occurs if the file does
fs.open(file, 'r+', function(err, fd) {
if (err) return callback(err);
function done(err) {
fs.close(fd, function(close_err) {
fd = null;
if (!err && close_err) {
// if no error passed in and there was a close error, return that
return callback(close_err);
} else {
// otherwise return error passed in
callback(err);
}
});
}
// file is open here, call done(err) when we're done to clean up open file
// get length of file so we know how to append
fs.fstat(fd, function(err, stats) {
if (err) return done(err);
// write data to the end of the file
fs.write(fd, data, stats.size, encoding, function(err) {
done(err);
});
});
});
}
You could, of course, just test to see if the file exists before calling fs.appendFile(), but that is not recommended because of race conditions. Instead, it is recommended that you set the right flags on fs.open() and let that trigger an error if the file does not exist.

Related

How do I modify JSON files while keeping the state updated?

If I have a program as follows to modify a JSON file:
var fs = require('fs');
var dt = require('./dataWrite.json');
console.log("before",dt);
fs.readFile('./data.json', 'utf8', (err, data) => {
if (err) {
throw err;
}
else {
fs.writeFileSync('./dataWrite.json', data);
}
});
console.log("after",dt);
The console for the before and after gives me the same results. The data in the file is modified as expected though. Is there a way to always have the latest state of the file in your program?
Side question: the following code doesn't modify the files at all, I wasn't able to figure why
var fs = require('fs');
var dt = fs.readFileSync('./dataTest.json', 'utf8', function (err, data) {
if (err) {
throw err;
}
});
console.log('before', dt);
fs.readFileSync('./data.json', 'utf8', (err, data) => {
if (err) {
throw err;
}
fs.writeFileSync('./dataTest.json', data);
console.log('data', data);
});
console.log("after", dt);
It's important here to distinguish between synchronous and asynchronous logic.
Since you are using require to read in the json file, the value of the file when the program executes is read in synchronously within dt, and read in once at the beginning of the program only.
When you use the fs.readFile API, you'll notice that it is an asynchronous API and that it requires you to provide a callback to handle the file's data. This means that any execution within it is handled at a later date.
As such, your before and after code will just print the same contents.
If you console.log(dt) after executing fs.writeFileSync you will still see the old value since dt is holding the old value known at the beginning of the program and not the latest value, but if you update the variable to the contents of the file after rereading the file, then you would see the latest contents.
e.g.
...
fs.writeFileSync('./dataWrite.json', data);
dt = fs.readFileSync('./dataWrite.json', 'utf8');
console.log(dt);
...
See fs.readFileSync.

NodeJS File System

modified an internal JSON file Object property via NodeJS File System through a dropdown on the UI and then when I read it again for further modification, the earlier modified value is not there. When I open the JSON file I can see the modified value but when I read the same file via 'fs.readfile' method, I debug it and I get to see the original value instead of the modified one.
Any suggestions on how to tackle this ?
I am calling this method from the respective place to read the file first and then write to it. This will be called from a separate module.
fileOperation: async function (operation, fileName, dataToWrite) {
switch (operation) {
case 'read':
return new Promise((resolve, reject) => {
fs.readFile(path.join(__dirname, '../../location/' + fileName), function (err, data) {
if (err) {
return reject(err)
} else {
const parsedData= JSON.parse(data);
return resolve(parsedData);
}
})
});
case 'write':
return new Promise((resolve, reject) => {
fs.writeFile(path.join(__dirname, '../../location/' + fileName), JSON.stringify(dataToWrite), {
'Content-Type': 'application/json'
}, function (error) {
if (error) {
return reject(error);
} else {
return resolve('File written successfully');
}
});
})
}
And on the index.js file, this one:
fs.readFile(path.join(__dirname, './location/fileName'), function (err, data) {
if (err) {
res.status(500).send(err);
} else {
fileData = JSON.parse(data).requiredKey;
}
The fileData above is not the one that I saved earlier.
Calling the methods from this switch case:
case 'dropdownSelection':
this.fileOperation('read', 'file.json', {}).then(fileData => {
fileData.key = updatedData;
this.fileOperation('write', 'file.json', fileData).then(success => {
return resolve(success);
}).catch(error => {
return reject(error)
})
}).catch(error => {
return reject(error)
})
break;
There are two likely scenarios I can think of given what you've shared in the question:
1. Timing Issue
It's possible that at the time that you read it again for further modification, the previous modification has yet to complete. File operations take time and are asynchronous, so if you don't handle it correctly, they can happen in an order that you don't expect.
The second code snippet you shared, in index.js, contains a file read. But it's not clear when that code runs. When it runs it will obtain a copy of the file as it was at that time - later updates executed by your dropdown selection will not automatically go back to the index.js unless you initiate another read after the write has completed.
2. Filepath Issue
The filepaths in your code snippets are different. In one case you have path.join(__dirname, '../../location/' + fileName) and in another case you have path.join(__dirname, './location/fileName').
It's possible that your file is writing to a location you aren't expecting. File reads and writes are done relative to current working directory. This means you might inadvertently be reading a different file than you're writing! Try navigating to the folder containing index.js, going up two folders, then into a location folder if one exists, and see if the file you're working with exists there. If it does, you'll know there's a filepath issue.

How to guarantee non-existance of a file before creating?

fs.exists is now deprecated for a decent reason that I should try to open a file and catch error to be sure nothing is possible to delete the file in between checking and opening. But if I need to create a new file instead of opening an existing file, how do I guarantee that there is no file before I try to create it?
You can't. You can however, create a new file or open an existing one if it exists:
fs.open("/path", "a+", function(err, data){ // open for reading and appending
if(err) return handleError(err);
// work with file here, if file does not exist it will be created
});
Alternatively, open it with "ax+" which will error if it already exists, letting you handle the error.
module.exports = fs.existsSync || function existsSync(filePath){
try{
fs.statSync(filePath);
}catch(err){
if(err.code == 'ENOENT') return false;
}
return true;
};
https://gist.github.com/FGRibreau/3323836
https://stackoverflow.com/a/31545073/2435443
fs = require('fs') ;
var path = 'sth' ;
fs.stat(path, function(err, stat) {
if (err) {
if ('ENOENT' == err.code) {
//file did'nt exist so for example send 404 to client
} else {
//it is a server error so for example send 500 to client
}
} else {
//every thing was ok so for example you can read it and send it to client
}
} );

Nodejs code giving error (err)

I'm using a nodejs code to generate a application package, but I want to check if a certain string is written in a text file. I only want to generate the application if the string is indeed in the text file.
I'm using this function to check that:
function checkValid(err, data) {
fs.readFile('http://domain.com/directory/data.txt', function (err, data) {
if (err) throw err;
if(data.indexOf('milk') < 0){
console.log(data)
}
});
}
//end of valid checker
And here I'm calling the function:
async.parallel(callItems, function(err, results) {
if(err) {
console.error("**** ERROR ****");
} else {
checkValid(err, data);
// Here I'm checking
createSourceDirectories(destDir, packageName);
copySourceDirectories(destDir, packageName);
removeBootstrapDirectories(destDir);
sendContentAsZip(destDir, res);
}
});
Please, you can check my whole code at: http://pastebin.com/2d7LtBgb
Everytime it returns an error, any idea? and preferably any (noob proof, I'm a very beginner) solutions.
Are you sure you can directly read a remote file with the "read" method?

node.js readfile woes

The following piece of code creates a text file and then reads it, overwrites it, and reads it again. Except the creation of the file the three I/O operations are performed using Node.js async readFile and writeFile.
I don't understand why the first read is returning no error but no data either. The output of this code is:
Starting...
Done.
first read returned EMPTY data!
write finished OK
second read returned data: updated text
Even if the operations were to happen in an arbitrary order (due to their async nature) I would have NOT expected to get an "empty data" object.
Any ideas why I am getting an empty data when reading the file (and no error) ?
Is there anything that I can do to make sure the file content is read?
var fs = require('fs');
var fileName = __dirname + '/test.txt';
// Create the test file (this is sync on purpose)
fs.writeFileSync(fileName, 'initial test text', 'utf8');
console.log("Starting...");
// Read async
fs.readFile(fileName, 'utf8', function(err, data) {
var msg = "";
if(err)
console.log("first read returned error: ", err);
else {
if (data === null)
console.log("first read returned NULL data!");
else if (data === "")
console.log("first read returned EMPTY data!");
else
console.log("first read returned data: ", data);
}
});
// Write async
fs.writeFile(fileName, 'updated text', 'utf8', function(err) {
var msg = "";
if(err)
console.log("write finished with error: ", err);
else
console.log("write finished OK");
});
// Read async
fs.readFile(fileName, 'utf8', function(err, data) {
var msg = "";
if(err)
console.log("second read returned error: ", err);
else
if (data === null)
console.log("second read returned NULL data!");
else if (data === "")
console.log("second read returned EMPTY data!");
else
console.log("second read returned data: ", data);
});
console.log("Done.");
Your code is asking for race conditions. Your first sync write is probably writing the file, but then your first read, second write, and second read are put onto the event loop simultaneously.
What could have happened here? First read gets read permission from the filesystem, second write gets write permission from the filesystem and immediately zeroes the file for future updating, then the first read reads the now empty file. Then the second write starts writing data and the second read doesn't get read permission until it's done.
If you want to avoid this, you need to use the flow:
fs.writeFileSync(filename, 'initial', 'utf8');
fs.readFile(filename, 'utf8', function(err, data) {
console.log(data);
fs.writeFile(filename, 'text', 'utf8', function(err) {
fs.readFile(filename, 'utf8', function(err, data) {
console.log(data);
});
});
});
If that "pyramid" insults your programming sensibilities (why wouldn't it?) use the async library's series function:
fs.writeFileSync(filename, 'initial', 'utf8');
async.series([
function(callback) {
fs.readFile(filename, 'utf8', callback);
},
function(callback) {
fs.writeFile(filename, 'text', 'utf8', callback);
},
function(callback) {
fs.readFile(filename, 'utf8', callback);
}
], function(err, results) {
if(err) console.log(err);
console.log(results); // Should be: ['initial', null, 'text']
});
EDIT: More compact, but also more "magical" to people not familiar with the async library and modern Javascript features:
fs.writeFileSync(filename, 'initial', 'utf8');
async.series([
fs.readFile.bind(this, filename, 'utf8'),
fs.writeFile.bind(this, filename, 'text', 'utf8'),
fs.readFile.bind(this, filename, 'utf8'),
], function(err, results) {
if(err) console.log(err);
console.log(results); // Should be: ['initial', null, 'text']
});
EDIT2: Serves me right for making that edit without looking up the definition of bind. The first parameter needs to be the this object (or whatever you want to use as this).
I had a similar problem. I was writing text to a file and had a change-handler telling me when the file had changed at which point I tried to read it ASYNC to process the new content of the file further.
Most of the time that worked but in some cases the callback for the ASYNC-read returned an empty string. So perhaps the changed-event happened before the file was fully written so when I tried to read it I got empty string. Now one could have hoped that the ASYNC read would have recognized that the file is in the process of being written and thus should wait until the write-operation was completed. Seems that in Node.js writing does not lock the file from being read so you get unexpected results if you try to read while write is going on.
I was able to GET AROUND this problem by detecting if the result of ASYNC read was empty string and if so do an additional SYNC-read on the same file. That seems to produce the correct content. Yes SYNC-read is slower, but I do it only if it seems that the ASYNC-read failed to produce the expected content.

Resources