1. 程式人生 > >基於Vue + Node.js + MongoDB的圖片上傳組件,實現圖片的預覽和刪除

基於Vue + Node.js + MongoDB的圖片上傳組件,實現圖片的預覽和刪除

信息 clas 發送數據 演示 新增 one input標簽 return tof

公司要寫一些為自身業務量身定制的的組件,要基於Vue,趁著這個機會,自己在業余時間也寫了個組件,選擇寫圖片上傳是因為自己之前一直對這個功能比較迷糊,所以這次好好了解了一下。演示在網址打開後的show.gif中。

使用技術:Vue.js | node.js | express | MongoDB。
github網址:https://github.com/capslocktao/private-project/tree/master/vue_uploader

技術分享圖片

功能

  • 單圖多圖上傳
  • 圖片上傳預覽
  • 上傳進度條
  • 分組上傳,分組查詢
  • 新建分組,刪除分組
  • 刪除圖片
  • 選擇圖片

目錄結構

技術分享圖片

前端利用Vue搭建,Entry.vue中引入子組件Upload.vue。在Upload.vue中,使用input標簽,上傳圖片,form表單提交數據,但是from讓人很頭疼,提交後刷新頁面,所以給它綁定了一個隱藏的iframe標簽來實現無刷新提交表單。

Dom中:

<form class="upload-content-right-top" enctype="multipart/form-data" ref="formSubmit" >
    <label class="upload-content-right-top-btn">上傳圖片</label>
    <input type="file" @change="uploadImage($event)" multiple="multiple" accept="image/gif, image/jpeg, image/png">
</form
> <iframe id="rfFrame" name="rfFrame" src="about:blank" style="display:none;"></iframe>

調用上傳函數提交完數據後:

upload();
document.forms[0].target="rfFrame";

圖片預覽
利用html5的fileReader對象

   let count = 0;//上傳函數外定義變量,記錄文件的數量,即遞歸的次數

/*-----------------------此段代碼在上傳函數中-------------------------------*/
   let fileReader 
= new FileReader(); //解析圖片路徑,實現預覽 fileReader.readAsDataURL(file.files[count]); fileReader.onload=()=>{ previewData = { url:fileReader.result,//圖片預覽的img標簽的src name:file.files[count].name, size:file.files[count].size, }; //這段代碼在上傳函數中,所以在外面定義了一個_this = this,fileList為vue的data中定義的狀態 _this.fileList.push(previewData); };

進度條實現
在axios的配置中定義onUploadProgress函數,接收參數:progressEvent,利用它的兩個屬性:progressEvent.total和progressEvent.loaded(上傳的文件總字節數和已上傳的字節數)
node寫接口,實現圖片的接收、查詢、刪除。實現分組的新增、查詢、刪除。利用Formidable模塊接收並處理前端傳過來的表單數據。利用fs模塊實現刪除文件功能。

let progress = 0;
let config = {
  headers: {‘Content-Type‘: ‘multipart/form-data‘},
  onUploadProgress (progressEvent){

    if(progressEvent.lengthComputable){
      progress = progressEvent.total/progressEvent.loaded;
      _this.$refs.progress[_this.$refs.progress.length-1].style.width = Number(progress).toFixed(2)*100+"%";
    }
  }
};

向formData中插入文件
formData = new FormData();
if(file.files[count]){
formData.append(‘file’,file.files[count],file.files[count].name);

對於上傳方式,我這裏統一采用依次上傳,無論是單圖多圖,單圖上傳一次就好,多圖則遞歸調用上傳函數,直到遞歸次數等於圖片數量,停止遞歸。

上傳函數

   let file=$event.target,
      formData = new FormData();
      //遞歸調用自身,實現多文件依次上傳
      let _this = this;
      let count = 0;
      let previewData = {};


    uploadImage($event){
      let file=$event.target,
      formData = new FormData();
      //遞歸調用自身,實現多文件依次上傳
      let _this = this;
      let count = 0;
      let previewData = {};

      function upload(){
        //開始上傳時,滾到底部
        _this.$refs.picWrapper.scrollTop = _this.$refs.picWrapper.scrollHeight;
        //定義axios配置信息
        let progress = 0;
        let config = {
          headers: {‘Content-Type‘: ‘multipart/form-data‘},
          onUploadProgress (progressEvent){
            console.log(`進度條的數量${_this.$refs.progress.length -1}`);
            if(progressEvent.lengthComputable){
              progress = progressEvent.total/progressEvent.loaded;
              //進度條
              _this.$refs.progress[_this.$refs.progress.length-1].style.width = Number(progress).toFixed(2)*100+"%";
            }
          }
        };
        //向formData中插入文件
        if(file.files[count]){
        formData.append(‘file‘,file.files[count],file.files[count].name);
        let fileReader = new FileReader();
        //解析圖片路徑,實現預覽
        fileReader.readAsDataURL(file.files[count]);
        fileReader.onload=()=>{
          previewData = {
            url:fileReader.result,
            name:file.files[count].name,
            size:file.files[count].size,
          };
          _this.fileList.push(previewData);
          _this.progressShow = true
        };
        fileReader.onloadend=()=>{
          //檢測圖片大小是否超出限制
          if(formData.get(‘file‘).size>_this.maxSize){
            formData.delete(‘file‘);
            //當圖片全部上傳完畢,停止遞歸
            count++;
            if(count > file.files.length-1){
              return
            }
            upload()
          }else{
              //發送數據
              axios.post(`/upload?mark=${_this.group}`,formData,config).then((response)=>{
                formData.delete(‘file‘);
                let res = response.data;
                console.log(res);
                if(res.result){
                  //如果是新建上傳
                  if(_this.group === ‘new‘){
                    _this.fileList.push(res.data);
                      _this.fileList.forEach((item,index)=>{
                          if(!item.newName){
                            _this.fileList.splice(index,1)
                          }
                      })

                    }else{
                    //如果是選擇其他組上傳,直接把返回數據賦值到文件數組
                      _this.fileList = res.data;
                    }

                  _this.newUpload = false
                }else{
                  alert(‘上傳失敗‘);
                  return;
                }
                _this.noPic = false;
                count++;
                if(count > file.files.length-1){
                  return
                }
                upload()
              }).catch((err)=>{
                alert(‘上傳失敗123‘);
              });
            }
        };
        }
      }
      //第一次調用
      upload();
      document.forms[0].target="rfFrame";

node.js後端實現

//引入表單處理模塊
let Formidable = require("formidable");
一系列定義…. 
form.encoding = ‘utf-8’; 
form.uploadDir = ‘/project/vue/vue_uploader/my-server/public/images’;//定義文件存放地址 
form.keepExtensions = true; 
form.multiples = false;//以單文件依次上傳的方式,實現多文件上傳 
form.maxFieldsSize = 1*1024; 
//解析圖片,重命名圖片名稱,返回給前端。 
let fileData = “”; 
let fileDir = “images”;//定義文件的存放路徑 
let route = ‘upload_’;//定義路由 
let serverIp = ‘http://localhost:3002/‘;//定義服務器IP

對文件數據進行處理,存入本地並存入數據庫(由於涉及到分組上傳。。。所以比較復雜)

解析文件函數: 
function handleFile (file){ 
let filename = file.name; 
let nameArray = filename.split(‘.’); 
let type = nameArray[nameArray.length-1]; 
let name = ”; 
for (let i = 0;i< nameArray.length - 1;i++){ 
name = name + nameArray[i]; 
} 
let date = new Date(); 
let time = ‘’ + date.getFullYear() + “” + date.getMonth() + “” + date.getDay() + “” + date.getHours() + “” + date.getMinutes() +”“+ date.getSeconds()+”_”+date.getMilliseconds(); 
let picName = name + time + ‘.’ + type; 
let newPath = form.uploadDir + “/” + picName; 
let oldPath = form.uploadDir + “/”+ file.path.substring(file.path.indexOf(route));
  fs.renameSync(oldPath, newPath); //重命名
    fileData = {
        id:`${new Date().getTime()}`,
        url:serverIp + newPath.substring(newPath.indexOf(fileDir)),
        name:file.name,
        size:file.size,
        isSelected:false,
        newName:picName,
    };
    UploadData.findOne({group:group},(err,doc)=>{
        if(err){
            res.json({
                result:false,
                msg:err.message
            })
        }else{
            if(doc){
                doc.picList.push(fileData);
                doc.save((err,saveResult)=>{

                    if(err){
                        return res.json({
                            result:false,
                        });
                    }else{
                        let length= doc.picList.length;
                        console.log(doc.picList.length)
                        if(groupMark === ‘all‘){
                            UploadData.find({},(err,queryResult)=>{
                                if(err){
                                    res.json({
                                        result:false,
                                        mgs:‘發生錯誤了‘
                                    })
                                }else{
                                    let allPic = [];
                                    queryResult.forEach((item)=>{
                                        if(item.group !==‘default‘){
                                            allPic = allPic.concat(item.picList)
                                        }
                                    });
                                        res.json({
                                            result:true,
                                            data:allPic.concat(queryResult[1].picList)
                                        })

                                }
                            })
                        }else if(groupMark === ‘new‘){

                            UploadData.findOne({group:‘default‘},(err,queryResult)=>{
                                if(err){
                                    return res.json({
                                        result:false,
                                        msg:err.message
                                    });
                                }else{
                                    return res.json({
                                        result:true,
                                        data:queryResult.picList[queryResult.picList.length-1]
                                    })
                                }
                            });

                        }else{
                            UploadData.findOne({group:group},(err,queryResult)=>{
                                if(err){
                                    return res.json({
                                        result:false,
                                        msg:err.message
                                    });
                                }else{
                                    return res.json({
                                        result:true,
                                        data:queryResult.picList
                                    })
                                }
                            });
                        }
                    }
                })

            }

        }

    })
}

最後,調用解析文件函數

form.parse(req,(err,fields,files)=>{ 
//傳多個文件 
if(files.file instanceof Array){ 
return 
}else{ 
//傳單個文件 
handleFile(files.file) 
} 
});

數據庫結構:

技術分享圖片

基於Vue + Node.js + MongoDB的圖片上傳組件,實現圖片的預覽和刪除