0

Is it possible to use res.download() after writing a file to the filesystem?

router.get('/exportjson', (req, res, next) => {
 let json = `{"@dope":[{"set":"","val":"200"}],"comment":"comment","folderType":"window"}`
 const file = `${__dirname}/upload-folder/export.JSON`;
 fs.writeFile('file', json, 'application/json', function(){
    res.download(file);
 })  
})
djs
  • 3,947
  • 3
  • 14
  • 28
Jeremy Nelson
  • 276
  • 8
  • 16

1 Answers1

1

I'm not sure I fully understand your question, but I'm assuming you want to be able to save that json data to the path /upload-folder/export.json and then allow the browser to download the file using res.download() at the path GET /exportjson.

You've got a couple of issues. First, fs.writeFile takes a file path as the first argument, and you are just passing the string file. With your code, the data would be written to the current directory as file. You probably want to use the path module and create a path to the file you want to write, like so:

const path = require('path');

const jsonFilePath = path.join(__dirname, '../upload-folder/export.json');

Assuming the code is at routes/index.js, this path would point to the root directory of the project to the file upload-folder/export.json.

The data you want to write is in your variable json, but you have it stored as a string. I would actually leave it as an object:

let json = {
  "@dope": [
    { 
      "set":"",
      "val":"200"
    }
  ],
  "comment":"comment",
  "folderType":"window"
};

And then call JSON.stringify on it when you pass it to fs.writeFile as the second argument. You will also need to pass in the utf-8 option as the third argument, not application/json:

fs.writeFile(jsonFilePath, JSON.stringify(json), 'utf-8', function(err) {

In the callback to fs.writeFile, you want to call res.download and pass it the path to the file that you just wrote to the filesystem, which is stored in jsonFilePath (you had this part right, I just changed the variable name):

res.download(jsonFilePath);

Here is the relevant portion of the router file that has code to get everything working correctly:

const fs = require('fs');
const path = require('path');

const jsonFilePath = path.join(__dirname, '../upload-folder/export.json');

router.get('/exportjson', (req, res, next) => {

  let json = {
    "@dope": [
      { 
        "set":"",
        "val":"200"
      }
    ],
    "comment":"comment",
    "folderType":"window"
  };

  fs.writeFile(jsonFilePath, JSON.stringify(json), 'utf-8', function(err) {
    if (err) return console.log(err);
    res.download(jsonFilePath);
  });  

});

Assuming this file lives in /routes/index.js, the file would be saved at /upload-folder/export.json.

Here is a gif showing how it looks on my machine:

exportJson

djs
  • 3,947
  • 3
  • 14
  • 28
  • Thank you, when I go to the endpoint directly I get the download, but when I access the endpoint with an axios request, I get the json in a normal response. axios.get('/api/exportjson', {json}) – Jeremy Nelson Dec 18 '19 at 23:06
  • @JeremyNelson Your question doesn't ask about axios or front-end stuff. Can you post another question, or edit this one, and let me know what you're trying to accomplish? To get the browser to give you that download dialogue, you have to perform the request from the browser, not JS with fetch or axios, but you can do some work with Blob's and ObjectURL's to mock the native behavior. I have an answer where I covered it for a different use case: https://stackoverflow.com/questions/58923772/sending-an-excel-file-from-backend-to-frontend-and-download-it-at-the-frontend/58945412#58945412 – djs Dec 19 '19 at 17:59