2012年8月26日 星期日

上下傳 file 範例 - Node.js

一開始先要用 express 來產生 client 端上傳的 html form。如下。
var express = require('express');

var app = express( );
app.configure(function() {
        app.use(express.bodyParser({uploadDir: './'}));
});
app.listen(8800);

app.get('/upload', function(req, res) {
    res.write('<html><body><form method="post" enctype="multipart/form-data" action="/fileUpload">'
    +'<input type="file" name="uploadingFile"><br>'
    +'<input type="submit">'
    +'</form></body></html>');
    res.end();
});
// 將檔案命名為 fileServer.js
如此一來,可以用 Browser 打開 http://127.0.0.1:8800/upload 這樣一來就是執行上方的程式碼
app.get('/upload', function(req, res) {
    res.write('<html><body><form method="post" enctype="multipart/form-data" action="/fileUpload">'
    +'<input type="file" name="uploadingFile"><br>'
    +'<input type="submit">'
    +'</form></body></html>');
    res.end();
});
也就是回傳一個 HTML 的內容給 Browser
<html>
<body>
     <form method="post" enctype="multipart/form-data" action="/fileUpload">
         <input type="file" name="uploadingFile"><br>
         <input type="submit"> 
    </form>
</body>
</html>
 在 Firefox 上面會看到
這個 submit 按下去之後會去連結 /fileUpload 這個路徑,所以接下來就是要來處理這個 post method。在 fileServer.js 加入以下程式碼
var fs = require('fs');
app.post('/fileUpload', function(req, res) {

    var uploadedFile = req.files.uploadingFile;
        var tmpPath = uploadedFile.path;
        var targetPath = './' + uploadedFile.name;

        fs.rename(tmpPath, targetPath, function(err) {
            if (err) throw err;
               fs.unlink(tmpPath, function() {
                  
                   console.log('File Uploaded to ' + targetPath + ' - ' + uploadedFile.size + ' bytes');
            });
        });
    res.send('file upload is done.');
    res.end();
});
在這我們會用到檔案處理,所以要require('fs')
對於上傳檔案來說,express 會用 req.files 來暫存有關上傳檔案的資訊,在這個例子中會看到如下的訊息
{ uploadingFile:
   { size: 15198,
     path: '7fedaa5a4b44a499e2cfd29fc7c3be71',
     name: 'object.png',
     type: 'image/png',
     hash: false,
     lastModifiedDate: Mon Aug 27 2012 09:06:45 GMT+0800 (CST),
     _writeStream:
      { path: '7fedaa5a4b44a499e2cfd29fc7c3be71',
        fd: 10,
        writable: false,
        flags: 'w',
        encoding: 'binary',
        mode: 438,
        bytesWritten: 15198,
        busy: false,
        _queue: [],
        _open: [Function],
        drainable: true },
     length: [Getter],
     filename: [Getter],
     mime: [Getter]
    }
}
在這個 json 物件裡,第一個出現的 key 是 uploadingFile 也就是寫在 client form 的
<html>
<body>
     <form method="post" enctype="multipart/form-data" action="/fileUpload">
         <input type="file" name="uploadingFile"><br>
         <input type="submit"> 
    </form>
</body>
</html> 
上方的 name 之後的值,接著比較重要的是 express 會把收到的 file 放在一個暫存的路徑,寫在 req.files 中的 path 之後,為
path: '7fedaa5a4b44a499e2cfd29fc7c3be71',
我們接著看 fileServer.js 裡的程式碼
var uploadedFile = req.files.uploadingFile;
var tmpPath = uploadedFile.path;
var targetPath = './' + uploadedFile.name;
由 uploadingFile.path 取到的是 tmp 的資料夾,把這裡的東西搬到 targetPath 這裡。前提是 express 要做一個設定
var app = express( );
app.configure(function() {
        app.use(express.bodyParser({uploadDir: './'}));

});
app.listen(8800);
設定 body 中 uploadDir 的真實路徑。
fs.rename(tmpPath, targetPath, function(err) {
        if (err) throw err;
               fs.unlink(tmpPath, function() {
                  
               console.log('File Uploaded to ' + targetPath + ' - ' + uploadedFile.size + ' bytes');
        });
});
利用 fs.rename 把在 tmp 路徑的上傳檔案移到 targetPath 中。最後用 unlink 移除 在 tmpPath 裡的檔案。這樣上傳的 post 就處理完成了。可以上傳看看,會放在和 fileServer.js 同一個目錄。
再來處理下載的部分,新增程式碼在 fileServer.js

app.get('/data', function(req,res) {
    if (req.query.fileName) {
       
        var filename = req.query.fileName;
        console.log(filename);
   
        var mimeType = "image/png";
        res.writeHead(200, mimeType);

        var fileStream = fs.createReadStream(filename);
        fileStream.on('data', function (data) {
            res.write(data);
        });
        fileStream.on('end', function() {
            res.end();
        });
       
    }else{
        res.writeHead(404,{"Content-Type": "application/zip"});
        res.write("error");
        res.end();
    }
});

下載的部分打算用 get 來完成,如果使用者在 Browser 下達
http://127.0.0.1:8800/data?fileName=object.png
就是準備要下載 object.png 這個檔案,所以一開始用
if(req.query.fileName)
來確定有指定 file name

var mimeType = "image/png";
res.writeHead(200, mimeType);
 來和 Browser 說接下來要傳的是一個 png 檔
        var fileStream = fs.createReadStream(filename);
        fileStream.on('data', function (data) {
            res.write(data);
        });
        fileStream.on('end', function() {
            res.end();
        });
上方的部分是開始一個 write stream,然後在 data 的狀態下,利用 res.write() 把資料寫給 Browser。在 end 的時候結束和 browser 的通訊。
在 Server 的部分通常會先和 Client 說 Client 想下載的檔案大小如何,請如下指定
fs.stat(filename, function(error, stat) {
      if (error) { throw error; }
          res.writeHead(200, {
            'Content-Type' : 'image/png',
            'Content-Length' : stat.size
      });
});
其中 filename 指的是 request 想要下載的檔案路徑。
如此一來,下載的部分也完成了。一樣,程式碼放在 GitHub

沒有留言:

張貼留言