Send files using Express

By

Learn how to send a file as a download in Express using the Response.download() method, set a custom filename, and run a callback when done.

~~~

Express provides a handy method to transfer a file as attachment: Response.download().

Once a user hits a route that sends a file using this method, browsers will prompt the user for download, instead of showing the file in the page.

Behind the scenes, res.download() sets the Content-Disposition header to attachment, which is what tells the browser to save the file to disk. It also sets the right Content-Type based on the file extension.

app.get('/', (req, res) => res.download('./file.pdf'))

In the context of an app:

const express = require('express')
const app = express()

app.get('/', (req, res) => res.download('./file.pdf'))
app.listen(3000, () => console.log('Server ready'))

Setting a custom filename

You can set the file to be sent with a custom filename:

res.download('./file.pdf', 'user-facing-filename.pdf')

The first argument is the file on your disk. The second is the name the user sees in the download dialog. This is handy when your files are stored with generated names like report-2xk39a.pdf but you want the user to receive invoice.pdf.

Running code after the download

This method provides a callback function which you can use to execute code once the file has been sent:

res.download('./file.pdf', 'user-facing-filename.pdf', (err) => {
  if (err) {
    //handle error
    return
  } else {
    //do something
  }
})

One thing to check inside the error handler is res.headersSent. If the transfer failed midway, part of the response already reached the browser, and you can’t send a fresh error page any more. You can only log the problem at that point.

Be careful with relative paths

A path like ./file.pdf is resolved from the directory where you launched node, not from the directory of your JavaScript file.

Start the app from a different folder, for example with node src/server.js from the project root, and the download fails with an ENOENT (file not found) error.

The fix is to build an absolute path using __dirname, which always points to the folder containing the current file:

const path = require('path')

app.get('/', (req, res) => {
  res.download(path.join(__dirname, 'file.pdf'))
})

Now the file is found no matter where you start the server from.

One last thing. res.download() forces a download. If you want the browser to display the file inline when it can, like a PDF in the built-in viewer, use res.sendFile() instead.

~~~

Related posts about express: