檔案傳輸方式

紀錄如何上傳與下載檔案

Browser上傳資料到Server

Server side

使用Express and Multer模組(Multer是改良Busbuy模組,儲存檔案方式是用Streaming)

var express = require('express');
var multer = require('multer');
var app = express();
var storage = multer.diskStorage({
    destination: function(req, file, cb){
        cb(null, '')
    },
    filename: function(req, file, cb){
        cb(null,file.fieldname)
    }
})

var upload = multer({storage: storage}).single('profile');

app.post('/',function(req, res){
    upload(req, res, function(err){
        console.log(req.body);
        console.log(req.file);
        if(err){
            console.log(err);
        }else{
            res.status(201).json("success");
        }
    })
})
app.listen(8080);

Browser side

主要是透過XMLHttpRequest傳送資料,當然也可以用JQuery的AJAX
要注意的地方在於 官方文件說要使用FormData封裝檔案與資料傳輸。
另外我有加入了進度追蹤,詳細介紹請參考此連結

<html>
    <head>
    </head>
    <body>
        <form id="myform" name="myform" enctype='multipart/form-data'>
            <input type="file" name="profile">
            <input type="submit">
        </form>
        <script type="text/javascript">
            //This is for upload File
            function uploadFiles(url, files) {
              var formData = new FormData();

              for (var i = 0, file; file = files[i]; ++i) {
                formData.append('profile', file);
              }
              formData.append('test', 'hello world');

              var xhr = new XMLHttpRequest();
              xhr.open('POST', url, true);
              xhr.onload = function(e) { 
                  console.log(e);
              };
              xhr.upload.onprogress = function(e) {
                  if (e.lengthComputable) {
                      console.log((e.loaded / e.total) * 100);
                  }
              }

              xhr.send(formData);
            }

            document.querySelector('input[type="file"]').addEventListener('change',function(e) {
                console.log(e);
                  uploadFiles('http://127.0.0.1:8080/', this.files);
                  console.log("happen");
            }, false);
        </script>
    </body>

下載圖片

Server Side

這邊要注意的是"不能使用同步的方式讀取資料讀取-->造成性能低落"、"使用steam方式傳送資料->不然的話檔案太大,會很快吃光記憶體"。
根據這兩個原則以及此參考連結,實現以下作法

app.get('/profile', function(req, response){
    var filePath = path.join(__dirname, 'profile.png');
    console.log(filePath);
    var stat = fs.statSync(filePath);

    response.writeHead(200, {
        'Content-Type': 'image/png', 
        'Content-Length': stat.size
    });

    var readStream = fs.createReadStream(filePath);
    readStream.on('data', function(data) {
        var flushed = response.write(data);
        console.log(data);
        // Pause the read stream when the write stream gets saturated
        if(!flushed)
            readStream.pause();
    });

    response.on('drain', function() {
        // Resume the read stream when the write stream gets hungry 
        readStream.resume();    
    });

    readStream.on('end', function() {
        console.log('end');
        response.end();        
    });
});

Browser side

同樣使用XHR,這邊值得注意的是reponse type設為Blob,後來查發現Blob就是Binary large of Object,通常是指照片或是影音檔;瀏覽器操作Blob可以參考此連結

document.getElementById('downloadImg').addEventListener('click',function(e){
  window.URL = window.URL || window.webkitURL;  // Take care of vendor prefixes.
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'http://127.0.0.1:8080/profile', true);
  xhr.responseType = 'blob';

  xhr.onload = function(e) {
    if (this.status == 200) {
      var blob = this.response;

      var img = document.getElementById('profileImg');
      img.onload = function(e) {
        //這行會讓照片無法下載,但依然可以顯示,詳見連結
        window.URL.revokeObjectURL(img.src);
      };
      img.src = window.URL.createObjectURL(blob);
      document.body.appendChild(img);
    }
  };

  xhr.send();
});

*如果想要直接下載檔案到電腦,可以用 response.download(your_file_path),瀏覽器會自動跳出下載的對話筐。