Using "fs" to read JSON file throws "invalid json" Node.js - javascript

This makes me crazy. I don't know why but when I use the filesystem require('fs') to read valid Json file I get error: Invalid json. Here it is the code:
var fs = require('fs');
const FILE_NAME = './json.json'
/*
// This way it works!
var jsonData = require('./json.json')
console.log(jsonData);
*/
async function readFile(filePath){
return new Promise(function(resolve, reject){
fs.readFile(filePath, 'utf8', function(err, contents) {
if(err){
console.log("Cant read the file " + err);
reject(err)
}
else{
resolve(contents)
}
})
})
}
async function getNames(fileName){
readFile(fileName).then(function(data){
try {
console.log(`Type of data: ` + typeof data);
console.log("File data: " + data);
return JSON.parse(data);
} catch (error) {
throw new Error( "Invalid JSON: " + error);
}
}).then(function(data){
console.log(`FILE OBJECT: ` + data);
}).catch(function(err){
console.error(err);
})
}
getNames(FILE_NAME)
This is the file content:
{
"name": "riko"
}
This is the console output:
Type of data: string
File data: {
"name": "riko"
}
Error: Invalid JSON: SyntaxError: Unexpected token in JSON at position 0
at C:\Users\rojer\Desktop\Node\test\main.js:31:13
I know I could use var jsonData = require('./json.json'), but
I want to know the reason it doesn't work.
What if the JSON data is embedded somewhere in a regular text file.
There seems to be some garbage.
Please help.

This:
Error: Invalid JSON: SyntaxError: Unexpected token in JSON at position 0
at C:\Users\rojer\Desktop\Node\test\main.js:31:13
tells us that there's an invisible character at the beginning of the file, probably a byte order mark (BOM), that require is handling but your code isn't. If the file is really in UTF-8, that BOM will be \xEF\xBB\xBF. Once read as UTF-8 into a JavaScript string, that will be the code point \u{FEFF} (because JavaScript strings are UTF-16 [but tolerate invalid surrogate pairs]). Update: Your binary listing of it confirms that.
I can confirm that if I have a UTF-8 JSON file with a BOM, require reads it and handles the BOM, but readFile returns the contents of the with the BOM intact, which trips up JSON.parse.
You can check for the BOM and strip it off, see *** lines:
const UTF8_BOM = "\u{FEFF}"; // ***
async function getNames(fileName){
readFile(fileName).then(function(data){
try {
console.log(`Type of data: ` + typeof data);
if (data.startsWith(UTF8_BOM)) { // ***
data = data.substring(UTF8_BOM.length); // ***
}
console.log("File data: " + data);
return JSON.parse(data);
} catch (error) {
throw new Error( "Invalid JSON: " + error);
}
}).then(function(data){
console.log(`FILE OBJECT: ` + data);
}).catch(function(err){
console.error(err);
})
}
Alternately, if you don't want the BOM there, here's a quick and dirty tool to add/remove BOMs on UTF-8 files:
const fs = require("fs");
const UTF8_BOM = "\u{FEFF}";
const actions = new Map([
["-a", addBOM],
["-r", removeBOM],
["-t", toggleBOM]
]);
main();
function main() {
const filename = process.argv[2];
const option = process.argv[3] || "-t";
const action = actions.get(option);
if (!filename) {
help();
return;
}
if (!action) {
console.error(`Invalid option ${option}`);
help();
return;
}
fs.readFile(filename, 'utf-8', (err, data) => {
if (err) {
console.error(`${filename}: Error reading file: ${err}`);
return;
}
const hasBOM = data.startsWith(UTF8_BOM);
action(filename, data, hasBOM);
});
}
function writeResult(filename, data, toggle, successMessage, failMessage) {
fs.writeFile(filename, data, "utf-8", (err) => {
if (err) {
console.error(`${filename}: ${failMessage}: ${err}`);
} else {
console.log(`${filename}: ${successMessage}${toggle ? " (toggled)" : ""}`);
}
});
}
function addBOM(filename, data, hasBOM, toggle) {
if (hasBOM) {
console.log(`${filename}: Already has a BOM`);
} else {
writeResult(filename, UTF8_BOM + data, toggle, "Added BOM", "Error adding BOM");
}
}
function removeBOM(filename, data, hasBOM, toggle) {
if (!hasBOM) {
console.log(`${filename}: Already doesn't have a BOM`);
} else {
writeResult(filename, data.substring(UTF8_BOM.length), toggle, "Removed BOM", "Error removing BOM");
}
}
function toggleBOM(filename, data, hasBOM) {
if (hasBOM) {
removeBOM(filename, data, hasBOM, true);
} else {
addBOM(filename, data, hasBOM, true);
}
}
function help() {
console.log("Usage: node utf8bomtoggle [filename] {options}");
console.log("{options} can be:");
console.log(" -t Toggle a BOM [default]");
console.log(" -a Add a BOM if not present");
console.log(" -r Remove a BOM if present");
}

Related

Add square brackets at beginning and end of json file using Node.js

I have a JSON file and I want to add a square bracket at the beginning and end of JSON.
eg.
Input
{
"name": "Ram",
"age": 25
},
{
"name": "Laxman",
"age": 24
}
Expected output:
[
{
"name": "Ram",
"age": 25
},
{
"name": "Laxman",
"age": 24
}
]
this is a sample response, I am having a large JSON data in a file.
The best option (in my opinion) would be to open a new reader;
Open a BufferedReader
Append [
Append JSON file
Append ]
From there you can use the BufferedReader or write it into a new file.
So, we need to consider 2 situations here:
The first one is when you're responsible for creating this input file. Then, assuming you already have those objects in an array and just need to save the array itself instead of the individual objects using a for.
const fs = require('fs')
const objs = [ { "name": "Ram","age": 25},{ "name": "Laxman","age": 24} ]
const jsonData = JSON.stringify(objs)
fs.writeFile("inputFile.json", jsonData, (err) => {
if (err) {
console.log(err);
}
});
The second situation if when you don't have control to modify the input file in its creation, and are just transforming the file previously saved. In this case you'll need to completely rewrite the file due to fs limitations for positional "inserts". To do so, read the previous file into a buffer, prepend it with the opening bracket "[" and append the closing one "]" at the end. As follows:
const fs = require('fs')
const filename = 'inputFile.json'
const fileBuffer = fs.readFileSync(filename)
const newBuffer = Buffer.concat([Buffer.from('['), fileBuffer, Buffer.from(']')])
fs.writeFileSync(filename, newBuffer)
I had the same problem and after hours of searching, I found nothing so I wrote a custom code that works like a charm. Hope it helps!:)
const fs = require('fs')
var response = {};
const fsOps = async (params) => {
try {
const path = "tmp/" + params.user + ".json";
const data = params.data;
const chunksNumber = params.chunksNumber;
var chunkID = params.chunkID;
//ON FIRST CHUNK ADD [
if (chunkID === 1) {
fs.appendFile(
path, ("["), 'utf-8', function (err) {
if (err) throw err;
}
);
if (chunksNumber !== 1)
fs.appendFile(
path, JSON.stringify(data, null, 2) + ',', 'utf-8', function (err) {
if (err) throw err;
}
);
}
//WRITE CHUNKS
if (chunkID !== 1 && chunkID < chunksNumber) {
fs.appendFile(
path, JSON.stringify(data, null, 2) + ',', 'utf-8', function (err) {
if (err) throw err;
}
);
}
//ON LAST CHUNK WRITE THE LAST CHUNK AND ADD ]
if (chunkID === chunksNumber) {
console.log("LAST CHUNK")
fs.appendFile(
path, JSON.stringify(data, null, 2), 'utf-8', function (err) {
if (err) throw err;
}
);
//APPEND ] on the end of file
fs.appendFile(
path, ("]"), 'utf-8', function (err) {
if (err) throw err;
}
);
//READ THE FILE
fs.readFile(path, (err, data) => {
if (err) {
console.error(err)
return;
} else {
response = data;
}
})
//DELETE FILE
fs.unlink(path, (err) => {
if (err) {
console.error(err)
return err
}
})
}
//Return object with all the part data
return JSON.parse(response);
} catch (err) {
//IN CASE OF ERROR DELETE FILE
fs.unlink(path, (err) => {
if (err) {
console.error(err)
return err
}
})
return err;
}
}
module.exports = fsOps;

Fs operations with Yargs

I have to perform some operations with Yargs.For example-
1- Write in a file using fs module and for every write operation need to create a new file,
2-You must take i/p from user as fileName and keep saving fileNames in one array (array part is not done), in one separate text file
3-Next time when user enters the same fileName , if it exists ask again to give new fileName , and then same as Point 1.
I am facing issues with point 2, how to write as an array in text file, and how to call 'Please provide the fileName' again if user keeps on giving existing fileName.
So far I have done this-
const argv = require('yargs').argv;
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
if (argv._[0] == 'write') {
rl.question('Please provide the filename:=>', (fileName) => {
fs.writeFile('fileNameList.txt', fileName, err => {
if (err) {
console.log('Error occured');
return;
}
fs.writeFile(fileName, 'Hello', err => {
if (err) {
console.log('Error occurred');
return
}
});
});
rl.close();
});
}
else {
console.log('No write operation');
}
so, when user executes it like node index.js write, it will ask the fileName
you need to refactor your code into methods to show intent properly:
Check if file exists
function ifFileExists(filepath) {
try {
fs.accessSync(filepath, fs.constants.F_OK);
return true;
} catch (e) {
return false;
}
}
Ask for user input
function askForUserInput(message) {
rl.question(message, (fileName) => {
if (ifFileExists(fileName)) {
askForUserInput('File already exists, Please provide a new filename:=>');
} else {
writeToFile(fileName);
rl.close();
}
});
}
write to file
function writeToFile(fileName) {
fs.writeFile('fileNameList.txt', fileName, err => {
if (err) {
console.log('Error occured');
return;
}
fs.writeFile(fileName, 'Hello', err => {
if (err) {
console.log('Error occured');
return
}
});
});
}
use it
if (argv._[0] == 'write') {
askForUserInput('Please provide the filename:=>');
}
else {
console.log('No write operation');
}
your logic to write filenames in fileNameList.txt looks correct.
Have a look at this solution and see, to me it looks like since you have file name as entry you can simply write it to the file and when reading from file add to an array
node.js - how to write an array to file
and
node.js: read a text file into an array. (Each line an item in the array.)
const argv = require("yargs").argv;
const fs = require("fs");
const readline = require("readline");
function ifFileExists(fileName) {
return new Promise((resolve, reject) => {
fs.readFile("array.txt", function (err, arrayData) {
if (err) {
if (err.code === "ENOENT") {
handleWhenArrayFileNotFound(reject, resolve);
} else {
reject("file read error");
}
}
if (arrayData) {
handleWhenArrayExists(arrayData, resolve, fileName);
}
});
});
function handleWhenArrayFileNotFound(reject, resolve) {
let content = fileName;
content += "\n";
fs.writeFile("array.txt", content, (error) => {
if (error) {
console.log("Error occured");
reject("file write error");
}
rl.close();
resolve("created");
});
}
function handleWhenArrayExists(arrayData, resolve, fileName) {
if (fileNamePresentInArray(arrayData, fileName)) {
askForNewName("File already exists, Please provide a new filename:=>");
} else {
resolve("create file");
}
}
}
function fileNamePresentInArray(arrayData, fileName) {
var array = arrayData.toString().split("\n");
return array.includes(fileName);
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
function askForNewName(message) {
rl.question(message, (fileName) => {
fs.readFile("array.txt", function (err, arrayData) {
if (err) {
console.log("array.txt not found");
}
if (arrayData) {
if (fileNamePresentInArray(arrayData, fileName)) {
askForNewName(
"File already exists, Please provide a new filename:=>"
);
} else {
writeToFile(fileName);
rl.close();
}
}
});
});
}
function askForUserInput(message) {
rl.question(message, (fileName) => {
ifFileExists(fileName)
.then((res) => {
writeToFile(fileName, res);
})
.catch((err) => {
console.log(err);
});
});
}
function writeToFile(fileName, data) {
if (data !== "created") {
let content = fileName;
content += "\n";
fs.appendFile("array.txt", content, (err) => {
if (err) console.log(err);
});
}
fs.writeFile(fileName, "You are awesome", (err) => {
if (err) {
console.log("Error occured");
}
});
}
if (argv._[0] == "write") {
askForUserInput("Please provide the filename:=>");
} else {
console.log("No write operation");
}

NodeJS Html-pdf: fs.readfilesync how to async/await

I have a problem with my html-pdf document creation. The problem is that often the code runs to fast to complete the process of pdf-docutment creation. The Processes consists out of building an HTML-String by replacing placeholders in an Html file. Below you see the code what happens afterwards.
Object.keys(setter).forEach(function(element, key, _array) {
var regex = new RegExp(element, "g");
data = data.replace(regex, setter[element])
})
var result = data;
fs.writeFile(mergeFileRes, result, 'utf8', function (err) {
if(err) {
console.log(err);
return;
} else {
let html2 = fs.readFileSync(mergeFileRes, 'utf8');
let options = {
format: 'a4' ,
"directory" : "/tmp",
};
if(html2){
pdf.create(html2, options).toStream(function(err, stream2){
if(err) console.log(err);
stream2.pipe(res);
stream2.on('end', function () {
try{
fs.unlink(mergeFileRes)
console.log(3090, "deleted file");
}
catch (err){
console.log(3090, "Did not delete file");
}
});
});
} else {
}
}
});
My problem is that in many cases the html2 variable is not yet created before the pdf.create process starts. This is probably because the readFileSync takes too long to finish.
I was wondering, how can I fix this. How can I make the pdf.create wait for the readFileSync to finish and the html2 variable to be filled.
You can use fs.readFile to read the file asynchronously and html2 will be available within the callback function.
Object.keys(setter).forEach(function(element, key, _array) {
var regex = new RegExp(element, "g");
data = data.replace(regex, setter[element])
})
var result = data;
fs.writeFile(mergeFileRes, result, 'utf8', function (err) {
if(err) {
console.log(err);
return;
} else {
fs.readFile(mergeFileRes, 'utf8', function(err, html2){
if (err) throw err;
let options = {
format: 'a4' ,
"directory" : "/tmp",
};
pdf.create(html2, options).toStream(function(err, stream2){
if(err) console.log(err);
stream2.pipe(res);
stream2.on('end', function () {
try{
fs.unlink(mergeFileRes)
console.log(3090, "deleted file");
}
catch (err){
console.log(3090, "Did not delete file");
}
});
});
});
}
});

How to upload image using AFNetworking + Sails.js

I have some codes from node.js project, it contains a post /uploadAvatar router, and a uploadAvatar function:
uploadAvatar: function(req, res) {
req.file('avatar').upload({
// don't allow the total upload size to exceed ~10MB
maxBytes: 10000000
},function whenDone(err, uploadedFiles) {
if (err) {
console.log(err);
return res.negotiate(err);
}
// If no files were uploaded, respond with an error.
if (uploadedFiles.length === 0){
return res.badRequest('No file was uploaded');
}
var avatarFd = uploadedFiles[0].fd;
var json = {fd: avatarFd};
console.log(json);
res.json(200, json);
});
}
Right now I want to using this api to upload image using AFNetworking. Current the swift code like below:
func uploadAvatar(avatar: NSData, success: JSONBlock, fail: ErrorBlock) -> Void {
sessionManager.POST("/uploadAvatar", parameters: nil, constructingBodyWithBlock: { (formData) in
formData.appendPartWithFormData(avatar, name: "avatar")
}, progress: { (_) in
}, success: { (_, responseObject) in
let json = self.jsonFromResponseObject(responseObject)
if json != nil {
success(json)
}
}) { (_, error) in
if let msgData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as? NSData {
if let message = String(data: msgData, encoding: NSUTF8StringEncoding) {
print(message)
}
}
fail(error.localizedDescription);
}
}
The error response I get is: "No file was uploaded". It seems the node.js can't find my image data. What did I do wrong?

Checking if writeFileSync successfully wrote the file

I have a simple route defined with express.js:
exports.save = function (request, response)
{
var file = request.body.file;
var content = request.body.content;
var saved = false;
if (fs.existsSync( file ))
{
saved = fs.writeFileSync(file, content, 'utf8');
}
console.log( saved ); // undefined or false, never true
response.send(saved ? 200 : 500, saved ? 'saved' : 'error'); // 500, error
};
Is if (typeof saved === 'undefined') saved = true; the only option? Feels hacky.
According to node.js source-code fs.writeFileSync doesn't return anything.
It throws an Error object if something goes wrong. So you should write fs.writeFileSync(file, content, 'utf8'); within a try-catch block.
fs.writeFileSync does not return any value, if there is no exception happens that means the save succeeded; otherwise failed.
you may want to try the async version of file read
fs.exists(file, function (exists) {
if (exists) {
fs.writeFiles(file, content, 'utf-8', function (err) {
if (err) {
response.send("failed to save");
} else {
response.send("succeeded in saving");
}
} else {
console.log('file does not exists');
}
}
fs.exists(file, function (exists) {
if (exists) {
fs.writeFiles(file, content, err=> {
if (err) res.status(500).send({error: "failed to save"});
else res.status(200).send({message : "succeeded in saving"});
} else {
res.status(404).send({error: "file not exists"})
}
}
Use async instead of sync. This will work.

Categories

Resources