1. 程式人生 > 其它 >大檔案切片上傳(Vue+NodeJS)

大檔案切片上傳(Vue+NodeJS)

技術標籤:Nodejsvuenodejs大資料uploadjavascript

大檔案上傳時,前端切片,上傳後,後端組合

先上介面
在這裡插入圖片描述

前臺

<template>
  <div>
      <div style="margin:20px">
          <h2>Upload Large Files</h2>
          <br>
            <el-row>
                <el-col :span="4">
                <input id="uppic"    type="file" multiple @change="selectedFiles()"   ref="upload">
                    <label for="uppic">
                        <div id="wrapper">
                            <div id="cell">
                                <span>
                                
                                <i class="el-icon-folder-opened"></i>
                                Choose  Files
                                </span>
                            </div>
                        </div>
                    
                    </label>
                </el-col>
                <el-col :span="10" :offset="1">
                <el-button id="userbtn" class="bg-main tc userbtn" type="success" @click="upload">
                   <i class="el-icon-upload"></i>
                    Upload
                    </el-button>
                </el-col>
            </el-row>
            <el-row v-if="fileList.length!=0">
                 <el-table
                    :data="fileList"
                    style="width: 100%">
                    <el-table-column
                        prop="name"
                        label="Name"
                        width="500">
                    </el-table-column>
                    <el-table-column
                        prop="size"
                        label="Size (KB)"
                        width="180">
                    </el-table-column>
                    
                </el-table>
               
            </el-row>
            <el-row>
                <el-col :span="20">
                        <el-progress :text-inside="true" :stroke-width="26" :percentage="percentage"></el-progress>
                        
                </el-col>

            </el-row>
       </div>
         
        
  </div>
</template>

<script>
import async from 'async'
  export default {
    data: () => ({
      percentage:0,
      fileList:[]
    }),
    methods:{
         upload(){
            this.percentage=0
            let _this=this

            for(let k in this.$refs.upload.files){
                let file=this.$refs.upload.files[k],//上傳檔案主體
                name = file.name,        //檔名
                size = file.size,        //總大小
                succeed = 0;  //當前上傳數
                let shardSize = 2 *1024*1024,    //以2MB為一個分片
                shardCount = Math.ceil(size / shardSize);  //總片數

                /*生成上傳分片檔案順充,通過async.eachLimit()進行同步上傳
                    attr裡面是[0,1,2,3...,最後一位]    
                */
                let attr=[];
                for(let i=0;i<shardCount;++i){
                    attr.push(i);
                }
                async.eachLimit(attr,1,async function(item,callback){
                    let i=item;
                    let start = i * shardSize,//當前分片開始下標
                    end = Math.min(size, start + shardSize);//結束下標

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

                //api是後臺上傳介面
                    await _this.$axios.post(‘api’,form,{
                        timeout: 120*1000
                    })
                    .then(res=>{
                        ++succeed;
                            
                            /*返回code為0是成功上傳,1是請繼續上傳*/
                            if(res.data.code==0){
                                console.log(res.data.mssg);
                                 _this.percentage=0
                                  _this.fileList.shift()
                                 _this.$message({
                                   type:'success',
                                   message:'upload finish'+file.name,
                                   showClose:true
                                   
                               })
                            }else if(res.data.code==1){

                                console.log(res.data.msg);
                            }
                            //生成當前進度百分比
                            _this.percentage=Math.round(succeed/shardCount*100);
                            /*如果是線上,去掉定時,直接callback(),
                            這樣寫是為方便,本地測試看到進度條變化
                            因為本地做上傳測試是秒傳,沒有時間等待*/
                            // setTimeout(callback,50);
                            callback()
                    })

                },function(err){
                    console.log('上傳成功');
                });     



            }

           




        },
        selectedFiles(){
            console.log('selected',this.$refs.upload.files)
            this.fileList= [...this.$refs.upload.files]
        }
        

    },
    watch:{
        
    }
     
  }
</script>

<style scoped>
#uppic {
    width: 0.1px; 
    height: 0.1px; 
    opacity: 0; 
    overflow: hidden; 
    position: absolute; 
    z-index: -1;
}

.el-row {
    margin-bottom: 20px;
}
label{
    color: aquamarine;
    background-color: #796e02e3;
    height: 40px;
    display: block;
    text-align: center;
}
#wrapper {display:table;height:40px;margin:0 auto;color:rgb(255, 255, 255);}
#cell{display:table-cell; vertical-align:middle;}
</style>

後臺

exports.upload=function user(req,res,config){ 
     
    let fs=require('fs');
    let async = require('async');//非同步模組
    let formidable=require('formidable')
    let form=new formidable.IncomingForm();
    

    //設定編輯
    form.encoding = 'utf-8';

    let dirPath=__dirname+"/../uploadFiles/tep/";
     
    //設定檔案儲存路徑
    form.uploadDir = dirPath;
    //設定單檔案大小限制
   // form.maxFilesSize = 200 * 1024 * 1024;
    /*form.parse表單解析函式,fields是生成陣列用獲傳過引數,files是bolb檔名稱和路徑*/
    form.parse(req, function (err,fields,files) {
         files=files['data'];//獲取bolb檔案
         let index=fields['index'];//當前片數
         let total=fields['total'];//總片數
         let name=fields['name'];//檔名稱
         let url= dirPath+'/'+name.split('.')[0]+'_'+index+'.'+name.split('.')[1];//臨時bolb檔案新名字
         fs.renameSync(files.path,url);//修改臨時檔名字
    
         try{
            if(index==total){//當最後一個分片上傳成功,進行合併
                /*
                    檢查檔案是存在,如果存在,重新設定名稱
                */
                let uid=uuid.v4()
         
                fs.mkdirSync(__dirname+"/../uploadFiles/"+uid)
                let pathname=__dirname+"/../uploadFiles/"+uid+'/'+name;//上傳檔案存放位置和名稱
                fs.access(pathname,fs.F_OK,(err) => {
                    if(!err){   
                        let myDate=Date.now();
                        pathname=dirPath+'/'+myDate+name;
                        console.log(pathname);

                    }
                });
                //這裡定時,是做非同步序列,等上執行完後,再執行下面
                setTimeout(function(){
                    /*進行合併檔案,先建立可寫流,再把所有BOLB檔案讀出來,
                        流入可寫流,生成檔案
                        fs.createWriteStream建立可寫流   
                        aname是存放所有生成bolb檔案路徑陣列:
                        ['Uploads/img/3G.rar1','Uploads/img/3G.rar2',...]
                    */
                    let writeStream=fs.createWriteStream(pathname);
                    let aname=[];
                    for(let i=1;i<=total;i++){
                        let url=dirPath+'/'+name.split('.')[0]+'_'+i+'.'+name.split('.')[1];
                        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();



                        //返回給客服端,上傳成功
                        let data=JSON.stringify({'code':0,"data": {
                            "source_store_id": uid,
                            "file_name": name
                        }});
                        res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'}); 
                        res.end(data);//返回資料    
                    });
                },50);

            }else{//還沒有上傳檔案,請繼續上傳
                let data=JSON.stringify({'code':1,'msg':'繼續上傳'});
                res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'}); 
                res.end(data);//返回資料    
            }
         }catch(err){
             console.log(err)
         }
       
    });
   
};