98

I Googled this but couldn't find an answer but it must be a common problem. This is the same question as Node request (read image stream - pipe back to response), which is unanswered.

How do I send an image file as an Express .send() response? I need to map RESTful urls to images - but how do I send the binary file with the right headers? E.g.,

<img src='/report/378334e22/e33423222' />

Calls...

app.get('/report/:chart_id/:user_id', function (req, res) {
     //authenticate user_id, get chart_id obfuscated url
     //send image binary with correct headers
});
Community
  • 1
  • 1
metalaureate
  • 7,572
  • 9
  • 54
  • 93

2 Answers2

139

There is an api in Express.

res.sendFile

app.get('/report/:chart_id/:user_id', function (req, res) {
    // res.sendFile(filepath);
});

http://expressjs.com/en/api.html#res.sendFile

Guillaume Vincent
  • 13,355
  • 13
  • 76
  • 103
Po-Ying Chen
  • 1,690
  • 1
  • 12
  • 12
  • 6
    instead of a file path could you do it from a stream? for instance if you had a variable that you were storing a file in so that you didn't have to actually save the file on the server? – BRogers Feb 07 '15 at 20:46
  • 8
    @BRogers `res` is a writable stream, so if you have a `Buffer` object or a `string` then you can use `.write` method to send it to client. – Po-Ying Chen Feb 08 '15 at 09:19
  • Thank you, I will be trying this. I have a CSV buffer that I want to send back to the client and it show as a downloaded file. I'll have to toy with that one. The file isn't very big, so I'm hoping to accomplish this. – BRogers Feb 08 '15 at 17:59
  • 2
    That worked beautifully! Just had to remember to set the `Content-Type`. Thank you! – BRogers Feb 08 '15 at 18:17
  • 9
    res.sendfile is deprecated now, the preferred method is res.sendFile: http://expressjs.com/api.html#res.sendFile – Jason Shultz Jul 11 '15 at 19:16
  • @BRogers or anybody: Can you pls help me with the writable stream code if you have ? i'm looking for help on downloading different formats(jpg, xls and etc). i've posted my question in forum as well http://stackoverflow.com/questions/34588839/download-all-file-formats-using-angualr-and-express-js. But no luck !!! – Rajesh Jan 07 '16 at 17:13
  • Crucially, it has to be an absolute file path, otherwise you get a (in this context) cryptic `Error: No default engine was specified and no extension was provided`. – Harry Mar 24 '17 at 16:37
  • What if you had multiple images? I'm a little hesitant to use Base64 Encoding. –  Jul 01 '19 at 21:21
22

a proper solution with streams and error handling is below:

const fs = require('fs')
const stream = require('stream')

app.get('/report/:chart_id/:user_id',(req, res) => {
  const r = fs.createReadStream('path to file') // or any other way to get a readable stream
  const ps = new stream.PassThrough() // <---- this makes a trick with stream error handling
  stream.pipeline(
   r,
   ps, // <---- this makes a trick with stream error handling
   (err) => {
    if (err) {
      console.log(err) // No such file or any other kind of error
      return res.sendStatus(400); 
    }
  })
  ps.pipe(res) // <---- this makes a trick with stream error handling
})

with Node older then 10 you will need to use pump instead of pipeline.

kharandziuk
  • 12,020
  • 17
  • 63
  • 121
  • 1
    I wonder what the real benefit would be here? Because Express `sendFile` does streaming under the hood as well. – aderchox Oct 30 '22 at 07:22
  • 3
    it changes nothing in the simple use case, but let's say you want to do some stream transformation on the fly. Like: zip/unzip a file or decrease the quality o an image on the fly. Those things are simple stream transformations. Did I answer your question? – kharandziuk Oct 31 '22 at 08:24