Завантажте файл з сервера 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
Просто FYI. Для використання у виробництві краще використовувати node.js позаду nginx і змусити nginx обробляти статичний вміст. Мабуть, це набагато краще підходить для цього.
Мунім

3
Голосування підтверджують, що немає такого поняття, як німе запитання :)
user2180794

2
@ user2180794 але є таке. Багато інших питань, на які позначено прапорці та голосування, є підтвердженням цього. Це питання, безумовно, не одне. Він відповідає правилам :)
Асимілятор

Питання, яке ви вказуєте, відрізняється, тут ОП хочуть повернути файл клієнту, тоді як це інше питання про те, як завантажити файл за допомогою сервера Node як клієнта (наприклад, файл від третьої сторони). У Lesast це те, що я зрозумів.
Ерік Бурель

Відповіді:


586

Оновлення

Експрес має помічника для цього, щоб полегшити життя.

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. Це набагато кращий спосіб робити речі, тому що при використанні будь-якого методу із «Синхронізацією» в імені нахмуриться, оскільки вузол має бути асинхронним.


3
Дякую .. Чи є спосіб отримати цю інформацію з fs.readFileSync? Я використовую статичний файл у цьому прикладі, але я буду використовувати цей файл завантаження для будь-яких файлів, передаючи його ім'я.
Тіаго Міранда де Олівейра

Встановлення назви вихідного файлу працює з res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Скопіюйте

як завантажити кілька документів за допомогою методу 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);    
});

6
Що робити, якщо дані надходять із запиту HTTP замість файлу, і ми повинні були дозволити користувачам завантажувати файл потоково?
літоночі

1
@summerNight - ну це інший випадок, ніж вказане питання. шукати nodejs proxy file download responseкращу практику
Жозеф Харуш

15

Для статичних файлів, таких як pdfs, документи 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). Але для всього, що вимагає захищеного доступу, краще прийнятий метод. Взагалі кажучи про документи та файли, що містять інформацію, я б не рекомендував використовувати публічний метод.
Немблтон

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

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.