Laden Sie mit Express eine Datei von NodeJS Server herunter

317

Wie kann ich eine Datei auf meinem Server auf meinen Computer herunterladen, um auf eine Seite auf einem NodeJS-Server zuzugreifen?

Ich benutze das ExpressJS und habe Folgendes versucht:

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

Aber ich kann den Dateinamen und den Dateityp (oder die Erweiterung) nicht erhalten. Kann mir jemand dabei helfen?

Thiago Miranda de Oliveira
quelle
13
Nur zu deiner Information. Für die Verwendung in der Produktion ist es besser, node.js hinter nginx zu verwenden und nginx statischen Inhalt verarbeiten zu lassen. Anscheinend ist es viel besser dafür geeignet.
Munim
3
Die Up-
Votes
2
@ user2180794 aber es gibt so etwas. Viele andere Fragen, die markiert und abgelehnt werden, sind ein Beweis dafür. Diese Frage ist jedoch mit Sicherheit keine. Es entspricht den Richtlinien :)
Assimilater
Die Frage, auf die Sie hinweisen, ist anders. Hier möchte OP eine Datei an einen Client zurückgeben, während sich diese andere Frage darauf bezieht, wie eine Datei mit Ihrem Serverknoten als Client heruntergeladen wird (z. B. eine Datei von einem Drittanbieter). Bei lesast habe ich das verstanden.
Eric Burel

Antworten:

585

Aktualisieren

Express hat einen Helfer , der das Leben leichter macht.

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

Alte Antwort

Für Ihren Browser lautet der Dateiname nur "Download". Sie müssen ihm also weitere Informationen geben, indem Sie einen anderen HTTP-Header verwenden.

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

Möglicherweise möchten Sie auch einen MIME-Typ wie diesen senden:

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

Wenn Sie etwas ausführlicheres wollen, dann sind Sie hier.

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

Sie können den Header-Wert beliebig einstellen. In diesem Fall verwende ich eine MIME-Bibliothek - Node-MIME -, um den MIME-Typ der Datei zu überprüfen.

Ein weiterer wichtiger Punkt ist, dass ich Ihren Code geändert habe, um einen readStream zu verwenden. Dies ist eine viel bessere Möglichkeit, Dinge zu tun, da die Verwendung einer Methode mit 'Sync' im Namen verpönt ist, da der Knoten asynchron sein soll.

loganfsmyth
quelle
3
Danke. Gibt es eine Möglichkeit, diese Informationen von fs.readFileSync abzurufen? In diesem Beispiel verwende ich eine statische Datei, aber ich verwende diese Download-API für alle Dateien, wobei der Name übergeben wird.
Thiago Miranda de Oliveira
Das Einstellen des Ausgabedateinamens funktioniert mit res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Capy
Herunterladen mehrerer Dokumente mit der Methode res.download ().
R J.
1
@RJ. Wenn Sie eine Frage haben, erstellen Sie eine neue, hinterlassen Sie keinen Kommentar.
Loganfsmyth
7
Express 4.x verwendet .set()anstelle von .setHeader()übrigens
Dana Woodman
48

Verwenden res.download()

Es überträgt die Datei im Pfad als "Anhang". Zum Beispiel:

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);    
});
Jossef Harush
quelle
6
Was ist, wenn die Daten von einer HTTP-Anfrage anstelle einer Datei eingehen und wir die Benutzer die Datei per Streaming herunterladen lassen müssen?
Sommernacht
1
@summerNight - nun, das ist ein anderer Fall als die angegebene Frage. Suche nach nodejs proxy file download responseBest Practice
Jossef Harush
15

Verwenden Sie für statische Dateien wie PDFs, Word-Dokumente usw. einfach die statische Funktion von Express in Ihrer Konfiguration:

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

Und dann legen Sie einfach alle Ihre Dateien in diesem 'öffentlichen' Ordner ab, zum Beispiel:

/public/docs/my_word_doc.docx

Und dann ermöglicht ein normaler alter Link dem Benutzer, ihn herunterzuladen:

<a href="public/docs/my_word_doc.docx">My Word Doc</a>
jordanb
quelle
1
Dies funktioniert gut für Assets (obwohl ein dedizierter Serving-Proxy wie Nginx empfohlen wird). Für alles, was einen gesicherten Zugriff erfordert, ist die akzeptierte Methode besser. Im Allgemeinen würde ich für Dokumente und Dateien, die Informationen enthalten, die Verwendung der öffentlichen Methode nicht empfehlen.
Nembleton
1
Sie können Middleware hinzufügen, um sicherzustellen, dass nur geeignete Benutzer auf die Dateien zugreifen können
MalcolmOcean
1
zB this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))... und dann geht jede Anfrage durch mEnsureAccess. Das bedeutet natürlich, dass Sie in der Lage sein müssen, die Zugriffsebene eines Benutzers nur anhand der URL des sicheren Dokuments oder was auch immer zu ermitteln.
MalcolmOcean
14

In Express 4.x gibt es eine attachment()Methode, um Response:

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

res.attachment('path/to/logo.png');
// Content-Disposition: attachment; filename="logo.png"
// Content-Type: image/png
Benoit Blanchon
quelle
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);
});
KARTHIKEYAN.A
quelle
4

So mache ich das:

  1. erstelle Datei
  2. Datei an Client senden
  3. Datei löschen

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!');
      });
    });
  });
};
Vedran
quelle
2
Dies ist die einzige Lösung, die ich in etwa zwei Tagen der Suche gefunden habe und die für mein spezielles Szenario zum Abrufen einer Audiodatei funktioniert. Das einzige ist, dass ich nicht denke, dass res.download () leider mit $ .ajax- Aufrufen funktioniert - ich musste verwenden window.open("/api/get_audio_file");, siehe: stackoverflow.com/a/20177012
user1063287