Загрузите файл с сервера NodeJS с помощью 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
Просто к вашему сведению. Для использования в производственной среде вам лучше использовать node.js за nginx и заставить nginx обрабатывать статический контент. По-видимому, это гораздо лучше подходит для обработки этого.
Munim
3
Голоса подтверждают, что
глупого
2
@ user2180794 но есть такая вещь. Многие другие вопросы, которые помечаются и ставятся на голосование, являются доказательством этого. Этот вопрос наверняка не один, хотя. Это соответствует руководящим принципам :)
Ассимилятор
Вопрос, на который вы указываете, отличается, здесь OP хочет вернуть файл клиенту, в то время как этот другой вопрос о том, как загрузить файл, используя ваш серверный узел в качестве клиента (например, файл от третьей стороны). По крайней мере, это то, что я понял.
Эрик Бурел

Ответы:

587

Обновить

У 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-типа, такие как это:

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-тип файла.

Еще одна важная вещь, на которую следует обратить внимание, это то, что я изменил ваш код для использования readStream. Это гораздо лучший способ сделать что-то, потому что использование любого метода с 'Sync' в имени не одобряется, потому что узел должен быть асинхронным.

loganfsmyth
источник
3
Спасибо .. Есть ли способ получить эту информацию от fs.readFileSync? Я использую статический файл в этом примере, но я буду использовать этот API загрузки для любых файлов, передавая его имя.
Тьяго Миранда де Оливейра
Настройка выходного имени файла работает с res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Capy
Как загрузить несколько документов, используя метод res.download ().
R J.
1
@RJ. Если у вас есть вопрос, создайте новый, не оставляйте комментарий.
loganfsmyth
7
Express 4.x использует .set()вместо .setHeader()Btw
Дана Вудман
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);    
});
Йосеф Харуш
источник
6
Что если данные поступают из HTTP-запроса, а не из файла, и мы должны были позволить пользователям загружать файл потоковым способом?
SummerNight
1
@summerNight - ну, это другой случай, чем указанный вопрос. поиск nodejs proxy file download responseлучшей практики
Йосеф Аруш
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>
jordanb
источник
1
Это хорошо работает для ресурсов (хотя рекомендуется выделенный прокси-сервер, такой как nginx). Но для всего, что требует защищенного доступа, принятый метод лучше. Вообще говоря, для документов и файлов, содержащих информацию, я бы не рекомендовал использовать публичный метод.
Нэмблтон
1
Вы можете добавить промежуточное ПО, чтобы обеспечить доступ к файлам только соответствующим пользователям
MalcolmOcean
1
например this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))... и затем каждый запрос проходит через mEnsureAccess. Конечно, это означает, что вам нужно будет определить уровень доступа пользователя только на основе URL защищенного документа или чего-либо еще.
MalcolmOcean
14

В Express 4.x есть attachment()способ 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);
});
KARTHIKEYAN.A
источник
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