Download a file from NodeJS Server using Express
Solution 1
Update
Express has a helper for this to make life easier.
app.get('/download', function(req, res){
const file = `${__dirname}/upload-folder/dramaticpenguin.MOV`;
res.download(file); // Set disposition and send it.
});
Old Answer
As far as your browser is concerned, the file's name is just 'download', so you need to give it more info by using another HTTP header.
res.setHeader('Content-disposition', 'attachment; filename=dramaticpenguin.MOV');
You may also want to send a mime-type such as this:
res.setHeader('Content-type', 'video/quicktime');
If you want something more in-depth, here ya go.
var path = require('path');
var mime = require('mime');
var fs = require('fs');
app.get('/download', function(req, res){
var file = __dirname + '/upload-folder/dramaticpenguin.MOV';
var filename = path.basename(file);
var mimetype = mime.lookup(file);
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
res.setHeader('Content-type', mimetype);
var filestream = fs.createReadStream(file);
filestream.pipe(res);
});
You can set the header value to whatever you like. In this case, I am using a mime-type library - node-mime, to check what the mime-type of the file is.
Another important thing to note here is that I have changed your code to use a readStream. This is a much better way to do things because using any method with 'Sync' in the name is frowned upon because node is meant to be asynchronous.
Solution 2
Use res.download()
It transfers the file at path as an “attachment”. For instance:
var express = require('express');
var router = express.Router();
// ...
router.get('/:id/download', function (req, res, next) {
var filePath = "/my/file/path/..."; // Or format the path using the `id` rest param
var fileName = "report.pdf"; // The default name the browser will use
res.download(filePath, fileName);
});
- Read more about
res.download()
Solution 3
For static files like pdfs, Word docs, etc. just use Express's static function in your config:
// Express config
var app = express().configure(function () {
this.use('/public', express.static('public')); // <-- This right here
});
And then just put all your files inside that 'public' folder, for example:
/public/docs/my_word_doc.docx
And then a regular old link will allow the user to download it:
<a href="public/docs/my_word_doc.docx">My Word Doc</a>
Solution 4
Here's how I do it:
- create file
- send file to client
- remove file
Code:
let fs = require('fs');
let path = require('path');
let myController = (req, res) => {
let filename = 'myFile.ext';
let absPath = path.join(__dirname, '/my_files/', filename);
let relPath = path.join('./my_files', filename); // path relative to server root
fs.writeFile(relPath, 'File content', (err) => {
if (err) {
console.log(err);
}
res.download(absPath, (err) => {
if (err) {
console.log(err);
}
fs.unlink(relPath, (err) => {
if (err) {
console.log(err);
}
console.log('FILE [' + filename + '] REMOVED!');
});
});
});
};
Solution 5
In Express 4.x, there is an attachment()
method to Response
:
res.attachment();
// Content-Disposition: attachment
res.attachment('path/to/logo.png');
// Content-Disposition: attachment; filename="logo.png"
// Content-Type: image/png
Thiago Miranda de Oliveira
Updated on July 18, 2022Comments
-
Thiago Miranda de Oliveira almost 2 years
How can I download a file that is in my server to my machine accessing a page in a nodeJS server?
I'm using the ExpressJS and I've been trying this:
app.get('/download', function(req, res){ var file = fs.readFileSync(__dirname + '/upload-folder/dramaticpenguin.MOV', 'binary'); res.setHeader('Content-Length', file.length); res.write(file, 'binary'); res.end(); });
But I can't get the file name and the file type ( or extension ). Can anyone help me with that?
-
Thiago Miranda de Oliveira over 12 yearsThanks.. Is there a way to get this information from the fs.readFileSync? I'm using a static file in this example but I'll use this download api for any files, passing the name of it.
-
Capy over 11 yearsSetting output filename works with
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
tnx! -
R J. about 11 yearshow to download multiple documents using res.download() method.
-
loganfsmyth about 11 years@RJ. If you have a question, create a new one, don't leave a comment.
-
frosty over 10 yearsWhat if you want to use res.download, but change the content type header? I am trying to serve a chrome extension of my site, but the Chrome web store only allows this to work if the content type of the file is 'application/x-chrome-extension'. So, using res.download, is it possible to do that?
-
loganfsmyth over 10 yearsPlease create a new question, comments are for commenting.
-
ZachB over 10 yearsNote that it appears that
res.download
does not use streaming. -
loganfsmyth over 10 years@ZachB What makes you say that? It does as far as I can see.
-
ZachB over 10 years@loganfsmyth I was reading the source code and it just calls
send
. See similar observations posted here: stackoverflow.com/questions/13106096/…. Would be nice if I'm wrong. -
loganfsmyth over 10 yearsI don't know about older versions of Express, but in
3.x
it just calls.sendfile
, which uses streaming. github.com/visionmedia/express/blob/… -
ZachB over 10 years@loganfsmyth Not sure what I was looking at, but you're right -- it does stream. Thanks for correcting me.
-
nembleton almost 9 yearsThat works well for assets (although a dedicated serving proxy like nginx is recommended). But for anything that requires secured access, the accepted method is better. Generally speaking for docs and files containing information, I wouldn't recommend using the public method.
-
MalcolmOcean over 8 yearsyou could add middleware to ensure that only appropriate users can access the files
-
MalcolmOcean over 8 yearse.g.
this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))
...and then each request goes through mEnsureAccess. Of course, that means you'll need to be able to figure out a user's access level just based on the url of the secure document, or whatever. -
user137717 over 8 years@loganfsmyth How can I set the file path with res.download? The file I want to send is a directory level above and __dirname+'../otherdir' isn't correct.
-
loganfsmyth over 8 yearsIf you have a question, please ask it as a question, don't leave a comment.
-
Dana Woodman about 8 yearsExpress 4.x uses
.set()
instead of.setHeader()
btw -
Besto over 7 yearsI couldn't do it with the first solution, but the express helper you edited in worked like a charm. Thanks!
-
summerNight over 6 yearsWhat if the data was coming in from a HTTP request instead of a file and we had to let users download the file in a streaming way?
-
Jossef Harush Kadouri over 6 years@summerNight - well, that is a different case than the question specified. search for
nodejs proxy file download response
for best practice -
MikeSchem almost 6 yearsif you pass in the path, is this method safe for directory traversal attacks?
-
loganfsmyth almost 6 years@MikeSchem It is not.
-
rohitwtbs over 5 years@loganfsmyth I just used this code to get docx on client.I am using angular5 in clent. I request the docx from a http get request.But when i get the file browser throws error. syntax error: uexpected token P at JSON position 0.
-
loganfsmyth over 5 years@rohitwtbs That sounds like you are treating to file as JSON when it isn't JSON. I can't really say why that would be.
-
user1063287 almost 5 yearsthis is the only solution i have found in about two days of searching that works for my particular scenario of getting an audio file. the only thing is that i don't think res.download() works with $.ajax calls unfortunately - i had to use
window.open("/api/get_audio_file");
, see: stackoverflow.com/a/20177012 -
1UC1F3R616 almost 4 years@summerNight i am still looking for this particular solution, if some link is there then plz help
-
summerNight almost 4 years@1UC1F3R616 I ended up solving the problem like this:
router.get(API_PREFIX + '/file-download', function (req, res, next) { var file = process.env.FILE_DOWNLOAD_LOCATION + '/' + req.query.filename res.download(file); });
-
Jossef Harush Kadouri almost 4 years@summerNight and @1UC1F3R616 note that you are vulnerable to directory traversal attacks. for instance
https://.../api?filename=../../../keys/my-secret-ssl-key.pem
. to avoid that, you need to validate the query param -
Manny Alvarado about 2 yearsI am baffled at this answer. I started my server with "node server.js". It's running, but I can't seem to get this working. This should download a file into your downloads folder in your computer no? In my setup it just doesn't work. I've been trying to do this for an hour now.
-
Jhollman almost 2 yearsthis was the Answer i looking for, Simple and Efficient