ดาวน์โหลดไฟล์จาก NodeJS Server โดยใช้ Express


318

ฉันจะดาวน์โหลดไฟล์ที่อยู่ในเซิร์ฟเวอร์ของฉันไปยังเครื่องของฉันเข้าถึงหน้าในเซิร์ฟเวอร์ 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
เพียงแค่ FYI สำหรับการใช้ในการผลิตคุณควรใช้ node.js ที่อยู่เบื้องหลัง nginx และทำให้ nginx จัดการกับเนื้อหาสแตติก เห็นได้ชัดว่ามันเหมาะสำหรับการจัดการ
Munim

3
คะแนนโหวตสูงขึ้นพิสูจน์ว่าไม่มีสิ่งเช่นคำถามโง่ :)
2180794

2
@ user2180794 แต่มีสิ่งนั้น คำถามอื่น ๆ อีกมากมายที่ได้รับการตั้งค่าสถานะและลงคะแนนเป็นข้อพิสูจน์ได้ แน่นอนว่าคำถามนี้ไม่ใช่คำถามเดียว มันตรงกับแนวทาง :)
Assimilater

คำถามที่คุณชี้ให้เห็นนั้นแตกต่างกันที่นี่ OP ต้องการส่งคืนไฟล์ไปยังลูกค้าในขณะที่คำถามอื่น ๆ เกี่ยวกับวิธีการดาวน์โหลดไฟล์โดยใช้โหนดเซิร์ฟเวอร์ของคุณในฐานะลูกค้า (เช่นไฟล์จากบุคคลที่สาม) อย่างน้อยนั่นคือสิ่งที่ฉันเข้าใจ
Eric Burel

คำตอบ:


586

ปรับปรุง

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-type เช่นนี้:

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-type

สิ่งสำคัญอีกประการที่ควรทราบที่นี่คือฉันได้เปลี่ยนรหัสของคุณเพื่อใช้ readStream นี่เป็นวิธีที่ดีกว่ามากในการทำสิ่งต่าง ๆ เนื่องจากการใช้วิธีการใด ๆ กับ 'ซิงค์' ในชื่อจะถูกดึงออกมาเนื่องจากโหนดมีความหมายว่าจะไม่ตรงกัน


3
ขอบคุณ .. มีวิธีรับข้อมูลนี้จาก fs.readFileSync หรือไม่ ฉันใช้ไฟล์คงที่ในตัวอย่างนี้ แต่ฉันจะใช้ api ดาวน์โหลดนี้สำหรับไฟล์ใด ๆ ผ่านชื่อของมัน
Thiago Miranda de Oliveira

ชื่อไฟล์การตั้งค่าการทำงานกับres.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Capy

วิธีการดาวน์โหลดเอกสารหลายฉบับโดยใช้วิธี res.download ()
R J.

1
@RJ หากคุณมีคำถามสร้างคำถามใหม่อย่าแสดงความคิดเห็น
loganfsmyth

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);    
});
  • อ่านเพิ่มเติมเกี่ยวกับ res.download()

6
เกิดอะไรขึ้นถ้าข้อมูลมาจากคำขอ HTTP แทนที่จะเป็นไฟล์และเราต้องให้ผู้ใช้ดาวน์โหลดไฟล์ด้วยวิธีสตรีมมิ่ง?
summerNightNight

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) แต่สำหรับทุกสิ่งที่ต้องการการเข้าถึงที่ปลอดภัยวิธีการที่ยอมรับจะดีกว่า โดยทั่วไปแล้วสำหรับเอกสารและไฟล์ที่มีข้อมูลฉันไม่แนะนำให้ใช้วิธีสาธารณะ
nembleton

1
คุณสามารถเพิ่มมิดเดิลแวร์เพื่อให้แน่ใจว่าเฉพาะผู้ใช้ที่เหมาะสมเท่านั้นที่สามารถเข้าถึงไฟล์ได้
MalcolmOcean

1
เช่นthis.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))... จากนั้นแต่ละคำขอจะต้องผ่าน mEnsureAccess แน่นอนนั่นหมายความว่าคุณจะต้องสามารถกำหนดระดับการเข้าถึงของผู้ใช้ตาม URL ของเอกสารที่ปลอดภัยหรืออะไรก็ตาม
MalcolmOcean


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

นี่คือวิธีที่ฉันทำ:

  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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.