Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解


Posted in Javascript onApril 29, 2020

本文实例讲述了Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能。分享给大家供大家参考,具体如下:

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

使用技术:Vue.js | node.js | express | MongoDB。

github网址:https://github.com/neroneroffy/private-project/tree/master/vue_uploader

Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解

功能

  • 单图多图上传
  • 图片上传预览
  • 上传进度条
  • 分组上传,分组查询
  • 新建分组,删除分组
  • 删除图片
  • 选择图片

目录结构

Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解

前端利用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图片上传组件实现图片预览和删除功能详解

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

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

希望本文所述对大家vue.js程序设计有所帮助。

Javascript 相关文章推荐
dess中一个简单的多路委托的实现
Jul 20 Javascript
jQuery ajax 路由和过滤器使用说明
Aug 02 Javascript
jQuery标签替换函数replaceWith()的使用例子
Aug 28 Javascript
jQuery对指定元素中指定字符串进行替换的方法
Mar 17 Javascript
JavaScript和JQuery的鼠标mouse事件冒泡处理
Jun 19 Javascript
AngularJs 指令详解及示例代码
Sep 01 Javascript
微信小程序 自定义对话框实例详解
Jan 20 Javascript
微信小程序实现图片轮播及文件上传
Apr 07 Javascript
JS实现带动画的回到顶部效果
Dec 28 Javascript
Angular angular-file-upload文件上传的示例代码
Aug 23 Javascript
vue生命周期和react生命周期对比【推荐】
Sep 19 Javascript
使用typescript快速开发一个cli的实现示例
Dec 09 Javascript
JS数组push、unshift、pop、shift方法的实现与使用方法示例
Apr 29 #Javascript
JS实现手写 forEach算法示例
Apr 29 #Javascript
Node.js API详解之 querystring用法实例分析
Apr 29 #Javascript
Node.js API详解之 string_decoder用法实例分析
Apr 29 #Javascript
深入浅析vue全局环境变量和模式
Apr 28 #Javascript
你准备好迎接vue3.0了吗
Apr 28 #Javascript
JavaScript禁止右击保存图片,禁止拖拽图片的实现代码
Apr 28 #Javascript
You might like
php数组函数序列之in_array() 查找数组值是否存在
2011/10/29 PHP
thinkphp的URL路由规则与配置实例
2014/11/26 PHP
php中stdClass的用法分析
2015/02/27 PHP
Yii中Model(模型)的创建及使用方法
2015/12/28 PHP
Symfony2框架学习笔记之表单用法详解
2016/03/18 PHP
PHP数据对象PDO操作技巧小结
2016/09/27 PHP
理清PHP在Linxu下执行时的文件权限方法
2017/06/07 PHP
Laravel如何使用Redis共享Session
2018/02/23 PHP
php常用经典函数集锦【数组、字符串、栈、队列、排序等】
2019/08/23 PHP
需要做特殊处理的DOM元素属性的访问
2010/11/05 Javascript
JavaScript Title、alt提示(Tips)实现源码解读
2010/12/12 Javascript
jquery datepicker参数介绍和示例
2014/04/15 Javascript
使用jQuery设置disabled属性与移除disabled属性
2014/08/21 Javascript
avalonjs实现仿微博的图片拖动特效
2015/05/06 Javascript
浅析AngularJS中的指令
2016/03/20 Javascript
使用jQuery.form.js/springmvc框架实现文件上传功能
2016/05/12 Javascript
基于JavaScript实现树形下拉框
2016/08/10 Javascript
使用layer模态框给新页面传值的方法
2019/09/27 Javascript
Vue分页效果与购物车功能
2019/12/13 Javascript
详解钉钉小程序组件之自定义模态框(弹窗封装实现)
2020/03/07 Javascript
关于小程序优化的一些建议(小结)
2020/12/10 Javascript
vue调用微信JSDK 扫一扫,相册等需要注意的事项
2021/01/03 Vue.js
pip 错误unused-command-line-argument-hard-error-in-future解决办法
2014/06/01 Python
Python操作SQLite简明教程
2014/07/10 Python
Python实现将一个正整数分解质因数的方法分析
2017/12/14 Python
wtfPython—Python中一组有趣微妙的代码【收藏】
2018/08/31 Python
pyinstaller还原python代码过程图解
2020/01/08 Python
python使用matplotlib:subplot绘制多个子图的示例
2020/09/24 Python
行政前台岗位职责
2013/12/04 职场文书
国际经济与贸易专业大学生职业规划书
2014/03/01 职场文书
学习雷锋精神心得体会范文
2014/03/12 职场文书
年终奖发放方案
2014/06/02 职场文书
领导干部整治奢华浪费之风思想汇报
2014/10/07 职场文书
2015年幼儿园中班工作总结
2015/04/25 职场文书
解除处分决定书
2015/06/25 职场文书
2019年大学推荐信
2019/06/24 职场文书