1

I want to use a Node/Express server to stream a file to the client as an attachment. I would like to make an async request from the client to a /download endpoint and then serve an object received via API proxy to the client as a downloadable file (similar to the way res.attachment(filename); res.send(body); behaves).

For example:

fetch(new Request('/download'))
    .then(() => console.log('download complete'))

app.get('/download', (req, res, next) => {
    // Request to external API
    request(config, (error, response, body) => {
        const jsonToSend = JSON.parse(body);
        res.download(jsonToSend, 'filename.json');
    })
});

This will not work because res.download() only accepts a path to a file. I want to send the response from an object in memory. How is this possible with existing Node/Express APIs?


Setting the appropriate headers does not trigger a download, either:

    res.setHeader('Content-disposition', 'attachment; filename=filename.json');
    res.setHeader('Content-type', 'application/json');
    res.send({some: 'json'});
Himmel
  • 3,629
  • 6
  • 40
  • 77

2 Answers2

5

This worked for me. I use the content type octet-stream to force the download. Tested on chrome the json was downloaded as 'data.json'
You can't make the download with ajax according to: Handle file download from ajax post

You can use a href / window.location / location.assign. This browser is going to detect the mime type application/octet-stream and won't change the actual page only trigger the download so you can wrap it a ajax success call.

//client
const endpoint = '/download';

fetch(endpoint, {
  method: 'POST',
  credentials: 'include',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  }
  })
  .then(res => res.json())
  .then(res => {
     //look like the json is good to download
     location.assign(endpoint);
   })
   .catch(e => {
     //json is invalid and other e
   });

//server
const http = require('http');

http.createServer(function (req, res) {
    const json = JSON.stringify({
      test: 'test'
    });
    const buf = Buffer.from(json);
    res.writeHead(200, {
      'Content-Type': 'application/octet-stream',
      'Content-disposition': 'attachment; filename=data.json'
    });
    res.write(buf);
    res.end();
}).listen(8888);
Community
  • 1
  • 1
mrdotb
  • 612
  • 6
  • 15
0

You can set the header to force the download, then use res.send

see those links

Community
  • 1
  • 1
user3
  • 740
  • 3
  • 15