基於node服務器的大文件(G級)上傳


原理如:3G的大文件分1500個2M二進度文件,通post方法發送給node服務,服務器全部接收到文件後,進組裝生成你上文件。

需要了解以下node庫,:

async: http://caolan.github.io/async/ 
multiparty:https://www.npmjs.com/package/multiparty

客戶端代碼:


<div class="hei-bg" style="display:block;" >
    <div class="user-info" style="display:block;"  >
        <div class="tc">請上傳大文件</div>
        <div class="user-pic picw320"><input id="uppic"  type="file" ><img id="upimg" src="/img/bbb.jpg"></div>
        <div  id="jd" class="jdb">進度</div>

        <div><div id="userbtn" class="bg-main tc userbtn">確定</div></div>
    </div>
</div>

<script src="./res/js/lib/jquery.min.js"></script>
<script src="./res/js/lib/async.min.js"></script>//異步庫


<script>
$(function(){
    $('#userbtn').on('click',function(){
            var file= $("#uppic")[0].files[0],//上傳文件主體
                name = file.name,        //文件名
                size = file.size,        //總大小
                succeed = 0;  //當前上傳數
                var shardSize = 2 *1024*1024,    //以2MB爲一個分片
                shardCount = Math.ceil(size / shardSize);  //總片數

            /*生成上傳分片文件順充,通過async.eachLimit()進行同步上傳
                attr裏面是[0,1,2,3...,最後一位]    
            */
            var attr=[];
            for(var i=0;i<shardCount;++i){
                attr.push(i);
            }



            async.eachLimit(attr,1,function(item,callback){
                 var i=item;
                 var start = i * shardSize,//當前分片開始下標
                 end = Math.min(size, start + shardSize);//結束下標

                //構造一個表單,FormData是HTML5新增的
                var form = new FormData();
                form.append("data", file.slice(start,end));  //slice方法用於切出文件的一部分
                form.append("name", name);//文件名字
                form.append("total", shardCount);  //總片數
                form.append("index", i + 1);   //當前片數
                //Ajax提交

                $.ajax({
                    url: "/dafile",
                    type: "POST",
                    data: form,
                    timeout:120*1000,
                    async: false,        //同步
                    processData: false,  //很重要,告訴jquery不要對form進行處理
                    contentType: false,  //很重要,指定爲false才能形成正確的Content-Type
                    success: function(data){
                        ++succeed;
                        var data=eval('('+data+')');
                        /*返回code爲0是成功上傳,1是請繼續上傳*/
                        if(data.code==0){
                            console.log(data.msg);
                        }else if(data.code==1){
                            console.log(data.msg);
                        }
                        //生成當前進度百分比
                        var jd=Math.round(succeed/shardCount*100)+'%';
                        $('.jdb').html(jd);
                        /*如果是線上,去掉定時,直接callback(),
                        這樣寫是爲方便,本地測試看到進度條變化
                        因爲本地做上傳測試是秒傳,沒有時間等待*/
                        setTimeout(callback,50);
                    }
                });     
            },function(err){
                console.log('上傳成功');
            });     
    });
});
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

服務器代碼:

function user(req,res,config){ 
    var path=require('path');   
    var fs=require('fs');
    var multiparty = require('multiparty');//文件上傳模塊
    var async = require('async');//異步模塊
    var form = new multiparty.Form();//新建表單

    //設置編輯
    form.encoding = 'utf-8';
    //設置文件存儲路徑
    form.uploadDir = "Uploads/img/";
    //設置單文件大小限制
   // form.maxFilesSize = 200 * 1024 * 1024;
    /*form.parse表單解析函數,fields是生成數組用獲傳過參數,files是bolb文件名稱和路徑*/
    form.parse(req, function (err,fields,files) {
         files=files['data'][0];//獲取bolb文件
         var index=fields['index'][0];//當前片數
         var total=fields['total'][0];//總片數
         var name=fields['name'][0];//文件名稱
         var url='Uploads/img/'+name+index;//臨時bolb文件新名字
         fs.renameSync(files.path,url);//修改臨時文件名字

         var pathname='Uploads/img/'+name;//上傳文件存放位置和名稱
         if(index==total){//當最後一個分片上傳成功,進行合併
                /*
                    檢查文件是存在,如果存在,重新設置名稱
                */
                fs.access(pathname,fs.F_OK,(err) => {
                    if(!err){   
                        var myDate=Date.now();
                        pathname='Uploads/img/'+myDate+name;
                        console.log(pathname);

                    }
                });
                //這裏定時,是做異步串行,等上執行完後,再執行下面
                setTimeout(function(){
                    /*進行合併文件,先創建可寫流,再把所有BOLB文件讀出來,
                        流入可寫流,生成文件
                        fs.createWriteStream創建可寫流   
                        aname是存放所有生成bolb文件路徑數組:
                        ['Uploads/img/3G.rar1','Uploads/img/3G.rar2',...]
                    */
                    var writeStream=fs.createWriteStream(pathname);
                    var aname=[];
                    for(var i=1;i<=total;i++){
                        var url='Uploads/img/'+name+i;
                        aname.push(url);
                    }

                    //async.eachLimit進行同步處理
                    async.eachLimit(aname,1,function(item,callback){
                        //item 當前路徑, callback爲回調函數
                        fs.readFile(item,function(err,data){    
                           if(err)throw err;
                           //把數據寫入流裏
                            writeStream.write(data);
                            //刪除生成臨時bolb文件              
                            fs.unlink(item,function(){console.log('刪除成功');})
                            callback();
                        });
                    },function(err){
                        if (err) throw err;
                        //後面文件寫完,關閉可寫流文件,文件已經成生完成
                        writeStream.end();
                        //返回給客服端,上傳成功
                        var data=JSON.stringify({'code':0,'msg':'上傳成功'});
                        res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'}); 
                        res.end(data);//返回數據    
                    });
                },50);

         }else{//還沒有上傳文件,請繼續上傳
            var data=JSON.stringify({'code':1,'msg':'繼續上傳'});
            res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'}); 
            res.end(data);//返回數據    
         }
    });
    return user;
}; 
exports.init = user;


這是初步設計方案,後期加異步上傳和斷點續傳功能。。

有什麼疑問或更好解決方案,可以加QQ羣和我交流: 293851491 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章