使用Express从NodeJS Server下载文件


317

如何将服务器中的文件下载到访问nodeJS服务器中页面的计算机上?

我正在使用ExpressJS,并且一直在尝试这样做:

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

但是我无法获取文件名和文件类型(或扩展名)。有人可以帮我吗?


13
仅供参考。为了在生产中使用,最好在nginx后面使用node.js,并让nginx处理静态内容。显然,它更适合处理该问题。
Munim 2013年

3
向上投票证明,没有像愚蠢的问题这样的事情:)
user2180794

2
@ user2180794但有这样的事情。许多其他被标记和否决的问题就是证明。这个问题最肯定不是。它符合准则:)
Assimilater

您指出的问题是不同的,这里OP希望将文件返回给客户端,而另一个问题是关于如何使用服务器节点作为客户端来下载文件(例如,来自第三方的文件)。至少这是我的理解。
Eric Burel

Answers:


585

更新资料

Express 为此提供了一个帮助,使生活更轻松。

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

旧答案

就您的浏览器而言,该文件的名称仅为“下载”,因此您需要使用另一个HTTP标头为其提供更多信息。

res.setHeader('Content-disposition', 'attachment; filename=dramaticpenguin.MOV');

您可能还希望发送如下的mime类型:

res.setHeader('Content-type', 'video/quicktime');

如果您想要更深入的了解,请继续。

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

您可以将标题值设置为任意值。在这种情况下,我使用的是mime类型的库-node-mime,以检查文件的mime类型。

这里要注意的另一件事是,我已将您的代码更改为使用readStream。这是一种更好的处理方式,因为由于node是异步的,因此不赞成使用名称中带有“ Sync”的任何方法。


3
谢谢..有没有办法从fs.readFileSync获取此信息?在此示例中,我使用的是静态文件,但是我将对所有文件使用此下载API,并传递其名称。
Thiago Miranda de Oliveira

设置输出文件名可与res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx 一起使用!
Capy 2013年

如何使用res.download()方法下载多个文档。
R J.

1
@RJ。如果有问题,请创建一个新问题,不要发表评论。
loganfsmyth 2013年

7
Express 4.x使用.set()而不是.setHeader()btw
Dana Woodman

48

采用 res.download()

它将路径中的文件作为“附件”传输。例如:

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

6
如果数据来自HTTP请求而不是文件,而我们不得不让用户以流方式下载文件怎么办?
summerNightSep

1
@summerNight-好吧,这与指定的问题不同。寻找nodejs proxy file download response最佳实践
Jossef Harush

15

对于pdf,Word文档等静态文件,只需在配置中使用Express的静态函数即可:

// Express config
var app = express().configure(function () {
    this.use('/public', express.static('public')); // <-- This right here
});

然后只需将所有文件放在该“公用”文件夹中即可,例如:

/public/docs/my_word_doc.docx

然后,常规的旧链接将允许用户下载它:

<a href="public/docs/my_word_doc.docx">My Word Doc</a>

1
这对资产效果很好(尽管建议使用专用的服务代理,例如nginx)。但是对于需要安全访问的任何内容,可接受的方法都更好。一般来说,对于包含信息的文档和文件,我不建议使用public方法。
nembleton

1
您可以添加中间件以确保只有适当的用户才能访问文件
MalcolmOcean 2015年

1
例如this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))...然后每个请求都通过mEnsureAccess。当然,这意味着您将需要能够仅基于安全文档的URL或其他来确定用户的访问级别。
MalcolmOcean 2015年

14

在Express 4.x中,有一种attachment()方法可以Response

res.attachment();
// Content-Disposition: attachment

res.attachment('path/to/logo.png');
// Content-Disposition: attachment; filename="logo.png"
// Content-Type: image/png

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

4

这是我的方法:

  1. 建立档案
  2. 发送文件给客户端
  3. 删除文件

码:

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

2
这是我在大约两天的搜索中找到的唯一解决方案,该解决方案适用于我获取音频文件的特定情况。唯一的事情是我不认为res.download()不幸地可以与$ .ajax调用一起使用-我不得不使用window.open("/api/get_audio_file");,请参阅: stackoverflow.com/a/20177012
user1063287
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.