ChatGPT解决这个技术问题 Extra ChatGPT

Download a file from NodeJS Server using Express

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?

Just FYI. For using in production, you are better off using node.js behind nginx, and make nginx handle static content. Apparently, it is much better suited for handling that.
@user2180794 but there is such a thing. Many other questions that get flagged and down-voted are proof of that. This question most certainly isn't one though. It matches the guidelines :)
The question you point out is different, here OP want to return a file to a client while this other question is about how to download a file using your server Node as a client (e.g a file from a 3rd party). At lesast that's what I understood.

A
Andre Christoga Pramaditya

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.


Thanks.. 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.
Setting output filename works with res.setHeader('Content-disposition', 'attachment; filename=' + filename); tnx!
how to download multiple documents using res.download() method.
@RJ. If you have a question, create a new one, don't leave a comment.
Express 4.x uses .set() instead of .setHeader() btw
J
Jossef Harush Kadouri

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()


What 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?
@summerNight - well, that is a different case than the question specified. search for nodejs proxy file download response for best practice
@summerNight i am still looking for this particular solution, if some link is there then plz help
@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); });
@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
j
jordanb

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>

That 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.
you could add middleware to ensure that only appropriate users can access the files
e.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.
V
Vedran

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!');
      });
    });
  });
};

this 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
B
Benoit Blanchon

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

K
KARTHIKEYAN.A
'use strict';

var express = require('express');
var fs = require('fs');
var compress = require('compression');
var bodyParser = require('body-parser');

var app = express();
app.set('port', 9999);
app.use(bodyParser.json({ limit: '1mb' }));
app.use(compress());

app.use(function (req, res, next) {
    req.setTimeout(3600000)
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept,' + Object.keys(req.headers).join());

    if (req.method === 'OPTIONS') {
        res.write(':)');
        res.end();
    } else next();
});

function readApp(req,res) {
  var file = req.originalUrl == "/read-android" ? "Android.apk" : "Ios.ipa",
      filePath = "/home/sony/Documents/docs/";
  fs.exists(filePath, function(exists){
      if (exists) {     
        res.writeHead(200, {
          "Content-Type": "application/octet-stream",
          "Content-Disposition" : "attachment; filename=" + file});
        fs.createReadStream(filePath + file).pipe(res);
      } else {
        res.writeHead(400, {"Content-Type": "text/plain"});
        res.end("ERROR File does NOT Exists.ipa");
      }
    });  
}

app.get('/read-android', function(req, res) {
    var u = {"originalUrl":req.originalUrl};
    readApp(u,res) 
});

app.get('/read-ios', function(req, res) {
    var u = {"originalUrl":req.originalUrl};
    readApp(u,res) 
});

var server = app.listen(app.get('port'), function() {
    console.log('Express server listening on port ' + server.address().port);
});

M
MD SHAYON

There are several ways to do it This is the better way

res.download('/report-12345.pdf')

or in your case this might be

app.get('/download', function(req, res){
  const file = `${__dirname}/upload-folder/dramaticpenguin.MOV`;
  res.download(file); // Set disposition and send it.
});

Know More


I 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.
a
abderrezague mohamed

you can use res.sendFile()... the Sample-download.xlsx should be in the same directory as this function.

const downloadFile = (req,res) => {   
            var options = {
              root: path.join(__dirname),
            };
            
            let fileName = "Sample-download.xlsx";
            res.sendFile(fileName, options, function (err) {
              if (err) {
                console.log(err);
                return res.status(500).json({ success: false, message: "internal server error. please try again later" });
            
              } else {
                console.log("Sent:", fileName, "at", new Date().toString());
              }
            });
    }