Téléchargez un fichier à partir du serveur NodeJS à l'aide d'Express

318

Comment puis-je télécharger un fichier qui se trouve sur mon serveur sur ma machine accédant à une page sur un serveur nodeJS?

J'utilise ExpressJS et j'ai essayé ceci:

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

Mais je ne peux pas obtenir le nom de fichier et le type de fichier (ou l'extension). Est-ce que quelqu'un peut m'aider avec cela?

Thiago Miranda de Oliveira
la source
13
Juste FYI. Pour une utilisation en production, il vaut mieux utiliser node.js derrière nginx et faire en sorte que nginx gère le contenu statique. Apparemment, il est beaucoup mieux adapté pour gérer cela.
Munim
3
Les votes
positifs
2
@ user2180794 mais il y a une telle chose. De nombreuses autres questions signalées et rejetées en sont la preuve. Cette question n'en est certainement pas une. Cela correspond aux directives :)
Assimilater
La question que vous signalez est différente, ici OP souhaite renvoyer un fichier à un client tandis que cette autre question concerne la façon de télécharger un fichier en utilisant votre serveur Node en tant que client (par exemple, un fichier d'un tiers). Au moins, c'est ce que j'ai compris.
Eric Burel

Réponses:

587

Mettre à jour

Express a une aide pour cela pour vous faciliter la vie.

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

Ancienne réponse

En ce qui concerne votre navigateur, le nom du fichier est simplement «télécharger», vous devez donc lui donner plus d'informations en utilisant un autre en-tête HTTP.

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

Vous pouvez également envoyer un type MIME comme celui-ci:

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

Si vous voulez quelque chose de plus approfondi, c'est parti.

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

Vous pouvez définir la valeur d'en-tête comme vous le souhaitez. Dans ce cas, j'utilise une bibliothèque de type mime - node-mime , pour vérifier quel est le type mime du fichier.

Une autre chose importante à noter ici est que j'ai changé votre code pour utiliser un readStream. C'est une bien meilleure façon de faire les choses, car l'utilisation de toute méthode avec «Sync» dans le nom est désapprouvée car le nœud est censé être asynchrone.

loganfsmyth
la source
3
Merci .. Existe-t-il un moyen d'obtenir ces informations à partir de fs.readFileSync? J'utilise un fichier statique dans cet exemple, mais je vais utiliser cette api de téléchargement pour tous les fichiers, en passant le nom de celui-ci.
Thiago Miranda de Oliveira
La définition du nom du fichier de sortie fonctionne avec res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Capy
comment télécharger plusieurs documents à l'aide de la méthode res.download ().
R J.
1
@RJ. Si vous avez une question, créez-en une, ne laissez pas de commentaire.
loganfsmyth
7
Express 4.x utilise .set()au lieu de .setHeader()btw
Dana Woodman
48

Utilisation res.download()

Il transfère le fichier sur path en tant que «pièce jointe». Par exemple:

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
la source
6
Et si les données provenaient d'une requête HTTP au lieu d'un fichier et que nous devions permettre aux utilisateurs de télécharger le fichier en streaming?
summerNight
1
@summerNight - eh bien, c'est un cas différent de la question spécifiée. recherche des nodejs proxy file download responsemeilleures pratiques
Jossef Harush
15

Pour les fichiers statiques comme pdfs, documents Word, etc., utilisez simplement la fonction statique d'Express dans votre configuration:

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

Et puis il suffit de mettre tous vos fichiers dans ce dossier «public», par exemple:

/public/docs/my_word_doc.docx

Et puis un ancien lien régulier permettra à l'utilisateur de le télécharger:

<a href="public/docs/my_word_doc.docx">My Word Doc</a>
jordanb
la source
1
Cela fonctionne bien pour les actifs (bien qu'un proxy de service dédié comme nginx soit recommandé). Mais pour tout ce qui nécessite un accès sécurisé, la méthode acceptée est meilleure. D'une manière générale pour les documents et les fichiers contenant des informations, je ne recommanderais pas d'utiliser la méthode publique.
nembleton
1
vous pouvez ajouter un middleware pour vous assurer que seuls les utilisateurs appropriés peuvent accéder aux fichiers
MalcolmOcean
1
par exemple this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))... et chaque demande passe par mEnsureAccess. Bien sûr, cela signifie que vous devrez être en mesure de déterminer le niveau d'accès d'un utilisateur uniquement en fonction de l'URL du document sécurisé, ou autre chose.
MalcolmOcean
14

Dans Express 4.x, il existe une attachment()méthode pour Response:

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

res.attachment('path/to/logo.png');
// Content-Disposition: attachment; filename="logo.png"
// Content-Type: image/png
Benoit Blanchon
la source
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
la source
5

Voici comment je le fais:

  1. créer un fichier
  2. envoyer le fichier au client
  3. effacer le fichier

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
la source
2
c'est la seule solution que j'ai trouvée en environ deux jours de recherche qui fonctionne pour mon scénario particulier d'obtenir un fichier audio. la seule chose est que je ne pense pas que res.download () fonctionne avec les appels $ .ajax malheureusement - je devais utiliser window.open("/api/get_audio_file");, voir: stackoverflow.com/a/20177012
user1063287