资讯专栏INFORMATION COLUMN

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

hzx / 2743人阅读

摘要:网址功能单图多图上传图片上传预览上传进度条分组上传,分组查询新建分组,删除分组删除图片选择图片目录结构前端利用搭建,中引入子组件。实现分组的新增查询删除。利用模块实现删除文件功能。

公司要写一些为自身业务量身定制的的组件,要基于Vue,写完后扩展了一下功能,选择写图片上传是因为自己之前一直对这个功能比较迷糊,所以这次好好了解了一下。演示在网址打开后的show.gif中。

使用技术:Vue.js | node.js | express | MongoDB。
github网址:https://github.com/neroneroff...

功能

单图多图上传

图片上传预览

上传进度条

分组上传,分组查询

新建分组,删除分组

删除图片

选择图片

目录结构

前端利用Vue搭建,Entry.vue中引入子组件Upload.vue。在Upload.vue中,使用input标签,上传图片,form表单提交数据,但是from让人很头疼,提交后刷新页面,所以给它绑定了一个隐藏的iframe标签来实现无刷新提交表单。

Dom中:

调用上传函数提交完数据后:

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{
        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)
    }
});  

数据库结构:

剩下的还有文件删除,新增分组,删除分组,分组查询的功能,由于篇幅有限,这些功能可以去看源码

第一次用node和mongoDB写后台业务,还有很多地方需要完善,代码会继续更新~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/19150.html

相关文章

  • 一些基于React、VueNode.jsMongoDB技术栈实践项目

    摘要:利用中间件实现异步请求,实现两个用户角色实时通信。目前还未深入了解的一些概念。往后会写更多的前后台联通的项目。删除分组会连同组内的所有图片一起删除。算是对自己上次用写后台的一个强化,项目文章在这里。后来一直没动,前些日子才把后续的完善。 欢迎访问我的个人网站:http://www.neroht.com/ 刚学vue和react时,利用业余时间写的关于这两个框架的训练,都相对简单,有的...

    tangr206 评论0 收藏0
  • 2017年3月份前端资源分享

    平日学习接触过的网站积累,以每月的形式发布。2017年以前看这个网址:http://www.kancloud.cn/jsfron... 03月份前端资源分享 1. Javascript 175453545 Redux compose and middleware 源码分析 深入 Promise(二)——进击的 Promise Effective JavaScript leeheys blog -...

    ermaoL 评论0 收藏0
  • 2017年3月份前端资源分享

    平日学习接触过的网站积累,以每月的形式发布。2017年以前看这个网址:http://www.kancloud.cn/jsfron... 03月份前端资源分享 1. Javascript 175453545 Redux compose and middleware 源码分析 深入 Promise(二)——进击的 Promise Effective JavaScript leeheys blog -...

    kamushin233 评论0 收藏0
  • 2017年3月份前端资源分享

    平日学习接触过的网站积累,以每月的形式发布。2017年以前看这个网址:http://www.kancloud.cn/jsfron... 03月份前端资源分享 1. Javascript 175453545 Redux compose and middleware 源码分析 深入 Promise(二)——进击的 Promise Effective JavaScript leeheys blog -...

    yy736044583 评论0 收藏0

发表评论

0条评论

hzx

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<