• 基于Vue + Node.js + MongoDB的图片上传组件,实现图片的预览和删除


    公司要写一些为自身业务量身定制的的组件,要基于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) 
    } 
    });

    数据库结构:

  • 相关阅读:
    c 的内存分配与释放原则: 通常应遵循“谁malloc,谁free”的原则。
    总算知道怎样从ImageMagick生成的数据转换成HICON: MagickGetImageBlob & LookupIconIdFromDirectoryEx
    收藏:Non-direct与direct ByteBuffer区别
    java NIO 直接与非直接缓冲区
    [收藏]:[算法]LRU和LFU的区别
    异步IO的并发能力:backlog的配置很重要
    ByteBuffer: 当由一个byte[]来生成一个固定不变的ByteBuffer时,使用ByteBuffer.wrap(byte[]);
    ByteBuffer的allocate与allocateDirect2013-01-11
    Windows完成端口与Linux epoll技术简介
    Java并发——Fork/Join框架与ForkJoinPool
  • 原文地址:https://www.cnblogs.com/catbrother/p/9397573.html
Copyright © 2020-2023  润新知