【问题标题】:Download a file from NodeJS Server using Express使用 Express 从 NodeJS 服务器下载文件
【发布时间】:2013-12-14 08:58:42
【问题描述】:

如何将服务器中的文件下载到访问 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();
});

但我无法获取文件名和文件类型(或扩展名)。谁能帮我解决这个问题?

【问题讨论】:

  • 仅供参考。在生产中使用,你最好在 nginx 后面使用 node.js,并让 nginx 处理静态内容。显然,它更适合处理这个问题。
  • 赞成票证明不存在愚蠢的问题 :)
  • @user2180794 但是有这样的事情。许多其他被标记和否决的问题就是证明。这个问题肯定不是一个。它符合准则:)
  • 您指出的问题不同,这里 OP 希望将文件返回给客户端,而另一个问题是关于如何使用服务器节点作为客户端下载文件(例如来自第三方)。至少我是这么理解的。

标签: javascript node.js file express download


【解决方案1】:

更新

表达has a helper,让生活更轻松。

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-type 库 - node-mime,来检查文件的 mime-type 是什么。

这里要注意的另一件重要事情是,我已将您的代码更改为使用 readStream。这是一种更好的做事方式,因为使用任何名称中带有“Sync”的方法都是不受欢迎的,因为节点是异步的。

【讨论】:

  • 谢谢.. 有没有办法从 fs.readFileSync 获取这些信息?我在这个例子中使用了一个静态文件,但我会将此下载 api 用于任何文件,并传递它的名称。
  • 设置输出文件名适用于res.setHeader('Content-disposition', 'attachment; filename=' + filename); tnx!
  • 如何使用 res.download() 方法下载多个文档。
  • @RJ。如果您有任何问题,请创建一个新问题,不要发表评论。
  • Express 4.x 使用.set() 而不是.setHeader() btw
【解决方案2】:

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

【讨论】:

  • 如果数据来自 HTTP 请求而不是文件,我们必须让用户以流式方式下载文件怎么办?
  • @summerNight - 好吧,这与指定的问题不同。搜索 nodejs proxy file download response 以获得最佳实践
  • @summerNight 我仍在寻找这个特殊的解决方案,如果有链接,请帮助
  • @1UC1F3R616 我最终解决了这样的问题:router.get(API_PREFIX + '/file-download', function (req, res, next) { var file = process.env.FILE_DOWNLOAD_LOCATION + '/' + req.query.filename res.download(file); });
  • @summerNight 和@1UC1F3R616 请注意,您容易受到目录遍历攻击。例如https://.../api?filename=../../../keys/my-secret-ssl-key.pem。为避免这种情况,您需要验证查询参数
【解决方案3】:

对于 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>

【讨论】:

  • 这适用于资产(尽管建议使用像 nginx 这样的专用服务代理)。但是对于任何需要安全访问的东西,接受的方法更好。一般来说,对于包含信息的文档和文件,我不建议使用 public 方法。
  • 您可以添加中间件以确保只有适当的用户才能访问文件
  • 例如this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret')) ...然后每个请求都通过 mEnsureAccess。当然,这意味着您需要能够仅根据安全文档的 url 或其他任何东西来确定用户的访问级别。
【解决方案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!');
      });
    });
  });
};

【讨论】:

  • 这是我在大约两天的搜索中找到的唯一解决方案,适用于我获取音频文件的特定场景。唯一的问题是我不认为res.download()$.ajax 通话很遗憾 - 我不得不使用window.open("/api/get_audio_file");,请参阅:stackoverflow.com/a/20177012
【解决方案5】:

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

    【讨论】:

      【解决方案7】:

      有几种方法可以做到这一点 这是更好的方法

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

      或者在你的情况下这可能是

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

      Know More

      【讨论】:

        【解决方案8】:

        假设您为用户上传的文件创建了一个表单。

        <form action="/upload" method="POST"  enctype="multipart/form-data">
        <input type="file" name="file"/>
        <input type="submit" value="upload"/>
        </form>
        

        enctype="multipart/form-data" 只是一个重要的数据编码协议,如果你想上传文件

        现在在服务器端,我们可以实现以下代码来获取上传的文件

        app.post('/upload', async (req, res) => {
        let file = req.files.file
        //dir is director where you want to move your file
        file.mv(dir, (error) => {
            if (error) {
                res.end("file could not be uploaded")
             }
           })
         })
        

        就是这样,上传的文件将被移动到所需的路径。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-03-30
          • 2017-07-06
          • 1970-01-01
          • 1970-01-01
          • 2015-10-18
          • 2015-06-14
          相关资源
          最近更新 更多