Unduh file dari NodeJS Server menggunakan Express

318

Bagaimana saya bisa mengunduh file yang ada di server saya ke mesin saya mengakses halaman di server nodeJS?

Saya menggunakan ExpressJS dan saya sudah mencoba ini:

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

Tapi saya tidak bisa mendapatkan nama file dan tipe file (atau ekstensi). Dapatkan seseorang menolong saya dengan itu?

Thiago Miranda de Oliveira
sumber
13
Hanya FYI. Untuk menggunakan dalam produksi, Anda lebih baik menggunakan node.js di belakang nginx, dan membuat nginx menangani konten statis. Rupanya, itu jauh lebih cocok untuk menangani itu.
Munim
3
Suara terbanyak membuktikan bahwa tidak ada pertanyaan bodoh :)
user2180794
2
@ user2180794 tetapi ada hal seperti itu. Banyak pertanyaan lain yang ditandai dan tidak dipilih adalah buktinya. Pertanyaan ini tentu saja bukan satu. Ini sesuai dengan pedoman :)
Assimilater
Pertanyaan yang Anda tunjukkan berbeda, di sini OP ingin mengembalikan file ke klien sementara pertanyaan lain ini adalah tentang cara mengunduh file menggunakan server Anda Node sebagai klien (misalnya file dari pihak ke-3). Setidaknya itu yang saya mengerti.
Eric Burel

Jawaban:

586

Memperbarui

Express memiliki penolong untuk hal ini untuk membuat hidup lebih mudah.

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

Jawaban Lama

Sejauh menyangkut browser Anda, nama file hanya 'unduh', jadi Anda perlu memberikan lebih banyak info dengan menggunakan header HTTP lain.

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

Anda mungkin juga ingin mengirim tipe pantomim seperti ini:

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

Jika Anda menginginkan sesuatu yang lebih mendalam, ini dia.

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

Anda dapat mengatur nilai header ke apa pun yang Anda suka. Dalam hal ini, saya menggunakan perpustakaan tipe-mime - node-mime , untuk memeriksa apa tipe-mime dari file tersebut.

Hal penting lain yang perlu diperhatikan di sini adalah bahwa saya telah mengubah kode Anda untuk menggunakan readStream. Ini adalah cara yang jauh lebih baik untuk melakukan sesuatu karena menggunakan metode apa pun dengan 'Sinkronisasi' dalam nama tidak disukai karena node dimaksudkan untuk tidak sinkron.

loganfsmyth
sumber
3
Terima kasih .. Apakah ada cara untuk mendapatkan informasi ini dari fs.readFileSync? Saya menggunakan file statis dalam contoh ini, tetapi saya akan menggunakan api unduhan ini untuk file apa pun, lewat nama itu.
Thiago Miranda de Oliveira
Mengatur nama file keluaran berfungsi dengan res.setHeader('Content-disposition', 'attachment; filename=' + filename);tnx!
Capy
cara mengunduh banyak dokumen menggunakan metode res.download ().
R J.
1
@RJ. Jika Anda memiliki pertanyaan, buat yang baru, jangan tinggalkan komentar.
loganfsmyth
7
Express 4.x gunakan .set()sebagai ganti .setHeader()btw
Dana Woodman
48

Menggunakan res.download()

Ini mentransfer file di jalur sebagai "lampiran". Misalnya:

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
sumber
6
Bagaimana jika data datang dari permintaan HTTP alih-alih file dan kami harus membiarkan pengguna mengunduh file dengan cara streaming?
summerNight
1
@ SummerNight - well, itu kasus yang berbeda dari pertanyaan yang ditentukan. cari nodejs proxy file download responsepraktik terbaik
Jossef Harush
15

Untuk file statis seperti pdf, dokumen Word, dll. Cukup gunakan fungsi statis Express di konfigurasi Anda:

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

Dan kemudian letakkan semua file Anda di dalam folder 'publik' itu, misalnya:

/public/docs/my_word_doc.docx

Dan kemudian tautan lama biasa akan memungkinkan pengguna untuk mengunduhnya:

<a href="public/docs/my_word_doc.docx">My Word Doc</a>
jordanb
sumber
1
Itu bekerja dengan baik untuk aset (meskipun proxy penyajian khusus seperti nginx disarankan). Tetapi untuk apa pun yang memerlukan akses aman, metode yang diterima lebih baik. Secara umum untuk dokumen dan file yang berisi informasi, saya tidak akan merekomendasikan menggunakan metode publik.
nembleton
1
Anda dapat menambahkan middleware untuk memastikan bahwa hanya pengguna yang tepat yang dapat mengakses file
MalcolmOcean
1
mis. this.use('/topsecret', mGetLoggedInUser, mEnsureAccess, express.static('topsecret'))... dan kemudian setiap permintaan melewati mEnsureAccess. Tentu saja, itu berarti Anda harus dapat mengetahui tingkat akses pengguna hanya berdasarkan url dokumen aman, atau apa pun.
MalcolmOcean
14

Di Express 4.x, ada attachment()metode untuk Response:

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

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

Begini cara saya melakukannya:

  1. buat file
  2. kirim file ke klien
  3. hapus file

Kode:

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
sumber
2
ini adalah satu-satunya solusi yang saya temukan dalam sekitar dua hari pencarian yang berfungsi untuk skenario khusus saya untuk mendapatkan file audio. satu-satunya hal adalah saya tidak berpikir res.download () berfungsi dengan $ .ajax panggilan sayangnya - saya harus menggunakan window.open("/api/get_audio_file");, lihat: stackoverflow.com/a/20177012
user1063287