Pobierz plik z serwera NodeJS za pomocą Express

317

Jak mogę pobrać plik znajdujący się na moim serwerze na mój komputer uzyskujący dostęp do strony na serwerze nodeJS?

Korzystam z ExpressJS i próbowałem tego:

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

Ale nie mogę uzyskać nazwy pliku i typu (lub rozszerzenia). Czy ktoś może mi w tym pomóc?

Thiago Miranda de Oliveira
źródło
13
Po prostu dla ciebie. Do użytku w produkcji lepiej jest użyć node.js za nginx i sprawić, by nginx obsługiwał zawartość statyczną. Najwyraźniej lepiej nadaje się do tego.
Munim,
3
Wyższe
2
@ user2180794, ale jest coś takiego. Dowodzi tego wiele innych pytań, które zostały oflagowane i odrzucone. To pytanie z pewnością nie jest jedno. Jest zgodny z wytycznymi :)
Assimilater,
Pytanie, które wskazałeś, jest inne, tutaj OP chce zwrócić plik klientowi, podczas gdy to drugie pytanie dotyczy tego, jak pobrać plik za pomocą węzła serwera jako klienta (np. Pliku od strony trzeciej). Co najmniej to zrozumiałem.
Eric Burel,

Odpowiedzi:

585

Aktualizacja

Express ma pomocnika, który ułatwia życie.

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

Stara odpowiedź

Jeśli chodzi o twoją przeglądarkę, nazwa pliku to po prostu „pobierz”, więc musisz podać mu więcej informacji, używając innego nagłówka HTTP.

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

Możesz również wysłać typ MIME, taki jak ten:

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

Jeśli chcesz czegoś bardziej dogłębnego, proszę bardzo.

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

Możesz ustawić wartość nagłówka na cokolwiek zechcesz. W tym przypadku używam biblioteki typu mime - node-mime , aby sprawdzić, jaki jest typ mime pliku.

Inną ważną rzeczą do odnotowania tutaj jest to, że zmieniłem twój kod, aby używał readStream. Jest to o wiele lepszy sposób na robienie rzeczy, ponieważ używanie dowolnej metody z 'Synchronizuj' w nazwie jest niezadowolone, ponieważ węzeł ma być asynchroniczny.

loganfsmyth
źródło
3
Dzięki .. Czy istnieje sposób na uzyskanie tych informacji z fs.readFileSync? Korzystam z pliku statycznego w tym przykładzie, ale użyję tego interfejsu API do pobierania plików, podając jego nazwę.
Thiago Miranda de Oliveira,
Ustawienie wyjściowej nazwy pliku działa z res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Capy
jak pobrać wiele dokumentów za pomocą metody res.download ().
R J.
1
@RJ. Jeśli masz pytanie, utwórz nowe, nie zostawiaj komentarza.
loganfsmyth
7
Express 4.x używa .set()zamiast .setHeader()btw
Dana Woodman
48

Posługiwać się res.download()

Przesyła plik pod ścieżką jako „załącznik”. Na przykład:

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
źródło
6
Co jeśli dane przychodziły z żądania HTTP zamiast pliku, a my musieliśmy umożliwić użytkownikom pobieranie pliku strumieniowo?
latoNoc
1
@summerNight - cóż, to inny przypadek niż określone pytanie. poszukaj nodejs proxy file download responsenajlepszych praktyk
Jossef Harush
15

W przypadku plików statycznych, takich jak pdf, Word Word itp. Wystarczy użyć funkcji statycznej Express w swojej konfiguracji:

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

A potem po prostu umieść wszystkie swoje pliki w tym „publicznym” folderze, na przykład:

/public/docs/my_word_doc.docx

A następnie zwykły stary link pozwoli użytkownikowi go pobrać:

<a href="public/docs/my_word_doc.docx">My Word Doc</a>
Jordan
źródło
1
Działa to dobrze w przypadku zasobów (chociaż zaleca się dedykowane serwery proxy, takie jak nginx). Ale w przypadku wszystkiego, co wymaga bezpiecznego dostępu, akceptowana metoda jest lepsza. Mówiąc ogólnie, w przypadku dokumentów i plików zawierających informacje, nie zalecałbym używania metody publicznej.
nembleton
1
możesz dodać oprogramowanie pośrednie, aby zapewnić dostęp tylko do odpowiednich użytkowników
MalcolmOcean
1
np. this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))... a następnie każde żądanie przechodzi przez mEnsureAccess. Oczywiście oznacza to, że musisz być w stanie określić poziom dostępu użytkownika na podstawie adresu URL bezpiecznego dokumentu lub cokolwiek innego.
MalcolmOcean,
14

W Express 4.x istnieje attachment()metoda Response:

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

res.attachment('path/to/logo.png');
// Content-Disposition: attachment; filename="logo.png"
// Content-Type: image/png
Benoit Blanchon
źródło
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
źródło
4

Oto jak to robię:

  1. utwórz plik
  2. wyślij plik do klienta
  3. Usuń plik

Kod:

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
źródło
2
jest to jedyne rozwiązanie, które znalazłem w ciągu około dwóch dni wyszukiwania, które działa w moim konkretnym scenariuszu uzyskania pliku audio. jedyną rzeczą jest to, że nie sądzę, że res.download () działa niestety z wywołaniami $ .ajax - musiałem użyć window.open("/api/get_audio_file");, patrz: stackoverflow.com/a/20177012
user1063287