Tải xuống tệp từ Máy chủ NodeJS bằng Express


318

Làm cách nào tôi có thể tải xuống một tệp trong máy chủ của mình vào máy của mình để truy cập một trang trong máy chủ nodeJS?

Tôi đang sử dụng ExpressJS và tôi đã thử điều này:

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

Nhưng tôi không thể lấy tên tệp và loại tệp (hoặc phần mở rộng). Bất cứ ai có thể giúp tôi với điều đó?


13
Chỉ cần FYI. Để sử dụng trong sản xuất, bạn nên sử dụng node.js phía sau nginx và làm cho nginx xử lý nội dung tĩnh. Rõ ràng, nó phù hợp hơn nhiều để xử lý điều đó.
Munim

3
Số phiếu tăng chứng minh rằng không có câu hỏi nào ngớ ngẩn :)
user2180794

2
@ user2180794 nhưng có một điều như vậy. Nhiều câu hỏi khác được gắn cờ và bỏ phiếu là bằng chứng về điều đó. Câu hỏi này chắc chắn không phải là một. Nó phù hợp với hướng dẫn :)
Assimilater

Câu hỏi bạn chỉ ra là khác nhau, ở đây OP muốn trả lại tệp cho khách hàng trong khi câu hỏi khác này là về cách tải xuống tệp bằng Node máy chủ của bạn dưới dạng máy khách (ví dụ: tệp từ bên thứ 3). Tại lesast đó là những gì tôi hiểu.
Eric Burel

Câu trả lời:


586

Cập nhật

Express có một người trợ giúp cho việc này để làm cho cuộc sống dễ dàng hơn.

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

Câu trả lời cũ

Theo như trình duyệt của bạn có liên quan, tên tệp chỉ là 'tải xuống', vì vậy bạn cần cung cấp thêm thông tin bằng cách sử dụng một tiêu đề HTTP khác.

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

Bạn cũng có thể muốn gửi một loại mime như thế này:

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

Nếu bạn muốn một cái gì đó sâu hơn, ở đây ya đi.

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

Bạn có thể đặt giá trị tiêu đề thành bất cứ điều gì bạn thích. Trong trường hợp này, tôi đang sử dụng một thư viện kiểu mime - nút-mime , để kiểm tra loại mime của tệp là gì.

Một điều quan trọng khác cần lưu ý ở đây là tôi đã thay đổi mã của bạn để sử dụng readStream. Đây là một cách tốt hơn để thực hiện mọi việc vì sử dụng bất kỳ phương thức nào có 'Đồng bộ hóa' trong tên đều được tán thành vì nút có nghĩa là không đồng bộ.


3
Cảm ơn .. Có cách nào để lấy thông tin này từ fs.readFileSync không? Tôi đang sử dụng một tệp tĩnh trong ví dụ này nhưng tôi sẽ sử dụng api tải xuống này cho bất kỳ tệp nào, truyền tên của nó.
Thiago Miranda de Oliveira

Đặt tên tệp đầu ra hoạt động với res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Capy

làm thế nào để tải xuống nhiều tài liệu bằng phương thức res.doad ().
R J.

1
@RJ. Nếu bạn có câu hỏi, hãy tạo một câu hỏi mới, đừng để lại nhận xét.
loganfsmyth

7
Express 4.x sử dụng .set()thay vì .setHeader()btw
Dana Woodman

48

Sử dụng res.download()

Nó chuyển tập tin tại đường dẫn dưới dạng tập tin đính kèm. Ví dụ:

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
Điều gì xảy ra nếu dữ liệu đến từ yêu cầu HTTP thay vì tệp và chúng tôi phải cho phép người dùng tải xuống tệp theo cách truyền phát?
hèNight

1
@summerNight - tốt, đó là một trường hợp khác với câu hỏi được chỉ định. tìm kiếm nodejs proxy file download responsethực hành tốt nhất
Jossef Harush

15

Đối với các tệp tĩnh như pdf, tài liệu Word, v.v. chỉ cần sử dụng hàm tĩnh của Express trong cấu hình của bạn:

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

Và sau đó chỉ cần đặt tất cả các tệp của bạn vào thư mục 'công khai' đó, ví dụ:

/public/docs/my_word_doc.docx

Và sau đó, một liên kết cũ thông thường sẽ cho phép người dùng tải xuống:

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

1
Điều đó hoạt động tốt cho các tài sản (mặc dù nên sử dụng proxy phục vụ chuyên dụng như nginx). Nhưng đối với bất cứ điều gì đòi hỏi truy cập an toàn, phương pháp được chấp nhận là tốt hơn. Nói chung đối với tài liệu và tệp chứa thông tin, tôi không khuyên bạn nên sử dụng phương thức chung.
nembleton

1
bạn có thể thêm phần mềm trung gian để đảm bảo rằng chỉ những người dùng phù hợp mới có thể truy cập các tệp
MalcolmOcean

1
ví dụ this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))... và sau đó mỗi yêu cầu đi qua mEnsureAccess. Tất nhiên, điều đó có nghĩa là bạn sẽ cần có thể tìm ra cấp độ truy cập của người dùng chỉ dựa trên url của tài liệu bảo mật hoặc bất cứ điều gì.
MalcolmOcean

14

Trong Express 4.x, có một attachment()phương thức để 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);
});

5

Đây là cách tôi làm điều đó:

  1. tạo tập tin
  2. gửi tập tin cho khách hàng
  3. xóa tập tin

Mã số:

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
đây là giải pháp duy nhất tôi tìm thấy trong khoảng hai ngày tìm kiếm phù hợp với kịch bản cụ thể của tôi là lấy tệp âm thanh. điều duy nhất là tôi không nghĩ res.doad () hoạt động với các cuộc gọi $ .ajax không may - tôi đã phải sử dụng window.open("/api/get_audio_file");, xem: stackoverflow.com/a/20177012
user1063287
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.