天天看點

nodejs 之檔案上傳

現在大多數的檔案上傳都是這樣的流程,用戶端->http->伺服器->ftp->CDN伺服器,下面按照這個流程來講解一下檔案上傳過程:

首先用戶端發起http請求,使用form Data格式上傳檔案資料,伺服器端接到請求,把檔案儲存到一個臨時檔案夾中,這裡我們用的是 nodejs 的 multer 子產品,該子產品功能很強大,

具體怎麼使用請打開傳送門 https://github.com/expressjs/multer,我貼一下我的代碼:

var cpUpload = upload.fields([{name: 'avatar',maxCount: 1 }, {name: 'images',maxCount: 4 }]);
var base = 'tmp/uploads';const fs = require('fs');
const logger = require('../../core/lib/logger')();
const multer = require('koa-multer');
const asyncUploadFtp = require('../lib/ftp')
const cryptoSimple = require('../../core/util/cypto-simple');
const employeeModel = require('../model/employee');

// multer上傳檔案配置
const storage = multer.diskStorage({
    destination:'./tmp/uploads',
    filename : function(req, file, cb){
        let ext = file.originalname.substring(file.originalname.lastIndexOf('.') + 1);
        cb(null,cryptoSimple.getMd5(file.originalname +Date.now()) +'.' + ext)
    }
})
const upload = multer({ 
    storage: storage, 
    limits: { fileSize: 1024 * 1024 * 2 },
    fileFilter : (req,file, cb) => {
        //過濾jpg/jpeg/png圖檔除外的檔案
        if(/.+[jpg|jpeg|png]$/.test(file.originalname)){
            cb(null,true);
        }else{
            cb(null,false)
        }
    } 
});
var cpUpload = upload.fields([{name: 'avatar',maxCount: 1 }, {name: 'images',maxCount: 4 }]);
var base = 'tmp/uploads';
           

調用cpUpload方法即可上傳檔案,我用的是koa2架構,ctx.req.files即可得到上傳的檔案資訊,這時tmp/loads檔案夾下已經可以發現我們上傳的檔案了

伺服器上傳到CDN伺服器用的ftp協定,用的是ftp-client子產品,直接上代碼

const _ = require('lodash');
const logger = require('../../core/lib/logger')();
var FtpClient = require('ftp-client');
var config = {
    host: '*.*.*.*',
    port: 21,
    user: '***',
    password: '***'
};
var base = 'tmp/uploads';
var source = base + '/**';
var target = 'hd/static/' + base;
var options = {
    logging: 'basic'
};
var upOption = {
    baseDir: base,
    overwrite: 'older'
}
module.exports =function asyncUploadFtp() {
    return new Promise((resolve,reject)=> {
        var fct = new FtpClient(config,options);
        fct.connect(function(){
            fct.upload(source,target, upOption, (result)=>{
                fct.ftp.end();
                if (_.isEmpty(result.errors) ===false) {
                    reject(result.errors);
                }
                resolve()
            })
        })
    }).catch(err=> logger.error(err));
};
           
所有的FTP伺服器都是需要帳号和密碼才能登入的。不過有相當一部分FTP伺服器提供了匿名登入,對于這些伺服器我們可以使用通用的帳号和密碼登入(通常是帳号Anonymous密碼anonymous),也許你登入這些FTP伺服器是沒有提示你輸入帳号和密碼,實際上Windows或者FTP軟體自動幫你完成了匿名登陸操作。   還有一部分FTP伺服器出于各種原因,沒有提供匿名登入,那麼你就需要手工登入了.      
下面比較一下http和ftp上傳檔案的差別:      

1.FTP

(1)FTP比HTTP複雜

FTP和HTTP一樣都是Internet上廣泛使用的協定,用來在兩台計算機之間互相傳送檔案。相比于HTTP,FTP協定要複雜得多。複雜的原因,是因為FTP協定要用到兩個TCP連接配接,一個是指令鍊路,用來在FTP用戶端與伺服器之間傳遞指令;另一個是資料鍊路,用來上傳或下載下傳資料。

(2)FTP協定有兩種工作方式:PORT方式和PASV方式,中文意思為主動式和被動式。

PORT(主動)方式的連接配接過程是:用戶端向伺服器的FTP端口(預設是21)發送連接配接請求,伺服器接受連接配接,建立一條指令鍊路。當需要傳送資料時,用戶端在指令鍊上用PORT指令告訴伺服器:“我打開了XXXX端口,你過來連接配接我”。于是伺服器從20端口向用戶端的XXXX端口發送連接配接請求,建立一條資料鍊路來傳送資料。

PASV(被動)方式的連接配接過程是:用戶端向伺服器的FTP端口(預設是21)發送連接配接請求,伺服器接受連接配接,建立一條指令鍊路。當需要傳送資料時,伺服器在指令鍊上用PASV指令告訴用戶端:“我打開了XXXX端口,你過來連接配接我”。于是用戶端向伺服器的XXXX端口發送連接配接請求,建立一條資料鍊路來傳送資料。

從上面可以看出,兩種方式的指令鍊路連接配接方法是一樣的,而資料鍊路的建立方法就完全不同。而FTP的複雜性就在于此。

2.上傳大檔案

Ftp有明顯的天生的優勢,不需要将檔案全部載入記憶體中,http在這方面就比較薄落了, 當然也有人實作了分塊http上傳,但總體來說http上傳對于大檔案不适合,多大算大呢?,在區域網路中,個人認為超過了500M的檔案就不适合使用http協定來上傳了。

3.上傳小檔案

非常适合http協定來上傳,因為簡單,而且可以實作更精細的控制,權限控制比ftp要簡單的多。而且

ftp每次上傳檔案都要建立一個新的ftp連接配接,對一個HTTP會話來講,用戶端可以維護一個單個的連接配接并使用它進行任意數量的資料傳輸。FTP每次有資料的都需要和伺服器建立一個新的連接配接。而且需要兩個TCP連接配接,重複的建立新的連接配接帶來的體驗并不好,因為每次建立連接配接都必須讓雙方握手驗證,這消耗了很多時間。是以上傳小檔案時http會更合适