iamleeadamson iamleeadamson - 4 months ago 10
Javascript Question

Node File System Create Directory and files on post route

I have a dashboard that generates JSON data and saves it as a .json file. This was initially written in PHP but for various reasons we have re-written the application in node. The code below takes the post data and then should check to see if the file exists then if it does update it if not it should create the file and directory.

However it only seems to create the first file and I cannot fathom why it doesn't create the subsequent files as this post route is called once for each post.

the post method looks like this

$.ajax({
type : "POST",
url : '/save/',
dataType : 'json',
data : {
category : settings.category_id,
name : settings.campaignId,
json : JSON.stringify(settings)
}
});


I have debugged and when called all the correct file paths are passed but its almost as if the file isn't being written with the data.

During debugging using node-inspector and nodemon the code loops through all the requested new file names and gives me the error code ENOENT, so it should then follow the create file path.

If you know anything about node and the file system module and feel like helping me out that would be amazing even if it's just pointing me in the direction of some more tutorials, ... anything would be great!

-

'use strict'

const fs = require('fs');
const path = require('path');
const express = require('express');
const router = express.Router();

/* Save Data */
router.post('/', function(req, res) {

if (!(req.body.json && req.body.name && req.body.category)) {
res.sendStatus(400);
return;
}

let dir = 'public/savedData/' + req.body.category;
let filepath = dir + '/' + req.body.name + '.json';

fs.access(filepath, function(error) {
console.log(filepath);

console.log(error.code);
if (error) {
if (error.code == 'ENOENT') {
console.log(error.code);
//debugger;
// Create file since it doesn't exist
createFile(req, res, filepath);

} else {
//debugger;
console.log('access error:', error);
res.sendStatus(500);
}
} else {
//debugger;
// Update file since it already exists
updateFile(req, res, filepath);
}
});
});

function createFile(req, res, filepath) {
try {
let json = JSON.parse(req.body.json);
let output = JSON.stringify([json], null, 4);

fs.mkdir(path.dirname(filepath), function(error) {
if (error) {
if (error.code == 'EEXIST') {
updateFile(req, res, filepath);
} else {
res.sendStatus(500);
console.log('create file error :', error);
}
} else {
fs.writeFile(filepath, output, function(error) {
if (error) {
res.sendStatus(500);
console.log('write file error :', error);
} else {
res.sendStatus(200);
console.log('Data successfully saved');
}
});
}
});
} catch (error) {
res.sendStatus(500);
console.log(error);
}
}

function updateFile(req, res, filepath) {

try {
fs.readFile(filepath, 'utf-8', function(error, data) {
if (error) {
res.sendStatus(500);
console.log('update error:', error);
} else {
try {
let newJSON = JSON.parse(req.body.json);
let jsonArray = JSON.parse(data);
let output;

jsonArray.push(newJSON);
output = JSON.stringify(jsonArray, null, 4);

fs.writeFile(filepath, output, function(error) {
if (error) {
res.sendStatus(500);
console.log(error);
} else {
res.sendStatus(200);
console.log('Data successfully saved');
}
});
} catch (error) {
res.sendStatus(500);
console.log(error);
}
}
});
} catch (error) {
res.sendStatus(500);
console.log(error);
}
}

module.exports = router;

Iso Iso
Answer

Instead of checking if the file exists, you should try to write with flags wx, which creates a file but fails if it does already exist. That way you won't be subjecting yourself to race conditions. I would also suggest the package mkdirp, which does not emit an error if the directory already exists.

router.post('/', (req, res) => {
  if (!(req.body.json && req.body.name && req.body.category)) {
    res.sendStatus(400);
    return;
  }

  const dirpath  = `public/savedData/${req.body.category}`;
  const filepath = `${dirpath}/${req.body.name}.json`;

  mkdirp(dirpath, err => {
    if (err) {
      console.error('mkdirp failed', err);
      return res.sendStatus(500);
    }

    const output = JSON.stringify([JSON.parse(req.body.json)]);

    fs.writeFile(filepath, output, { flags: 'wx' }, err => {
      if (err) {
        console.error('writeFile failed', err);
        return res.sendStatus(500);
      }

      console.log('Data successfully saved');
      res.sendStatus(200);
    });
  );
});

Make sure you sanitize the req.body.name and req.body.category parameters, since you could expose your filesystem to unintentional overwrites.