Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解
作者:nero 发布时间:2024-05-10 14:14:56
本文实例讲述了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搭建,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)
}
});
数据库结构:
剩下的还有文件删除,新增分组,删除分组,分组查询的功能,由于篇幅有限,这些功能可以去看源码
第一次用node和mongoDB写后台业务,还有很多地方需要完善,代码会继续更新~
希望本文所述对大家vue.js程序设计有所帮助。
来源:https://segmentfault.com/a/1190000012349559


猜你喜欢
- 在python开发的过程中,经常会遇到需要打印各种信息。海量的信息堆砌在控制台中,就会导致信息都混在一起,降低了重要信息的可读性。这时候,如
- RSA算法是一种非对称加密算法,是现在广泛使用的公钥加密算法,主要应用是加密信息和数字签名。 * 给出的RSA算法简介如下: 假设Alic
- 如果你有过Web编程的经验,那么或多或少都听说过或者使用过模板。简而言之,模板是可用于创建动态内容的文本文件。例如,你有一个网站导航栏的模板
- 我们知道,全局临时表的生命周期一直持续到创建会话(不是创建级别)才终止。有时候,你可能想创建一个不属于任何会话的全局临时表。而无论你进行什么
- 今天刚看到ChinaUI.com新改版的网站,觉得优艾网是越来越漂亮了,这次的变化的确是挺大的。一早大概的看了一下就关了,后来晚上又打开看时
- 一、安装库首先我们需要安装PIL和pytesseract库。PIL:(Python Imaging Library)是Python平台上的图
- 前言相信大家在日常工作生活中少不了和邮件打交道,比如我们会用邮件进行信息交流,向上级汇报日常工作;再比如大家熟悉的某个WEB系统注册阶段,通
- 大大小小也搞过一些数据库设计,见过一些其他人的设计,看过些书,总结总结,经验谈。选表类型:大家都知道mysql的myisam表适合读操作大,
- 目录一、线程基础以及守护进程二、线程锁(互斥锁)三、线程锁(递归锁)四、死锁五、队列六、相关面试题七、判断数据是否安全八、进程池 &
- 从这次开始,我会由简单到困难(其实也不会困难到哪里去)讲几个例程,每一个例程都是我自己写(或者修改,那样的话我会提供原始出处)的,都具有一定
- 如下所示:def read_data(file_name): ''' file_name:文件地址 '
- 理由:jquery简单,兼容性好且容易封装。废话不多说,马上开始我们的Jquery插件编写吧。应该有很多人写过类似的插件,我也是有些模仿fl
- 微信(WeChat) 是腾讯公司于2011年1月21日推出的一个为智能终端提供即时通讯服务的免费应用程序,由张小龙所带领的腾讯广州研发中心产
- PyCharm 中在使用Turtle(海龟)库绘图体系时,代码明明是正确可以运行的,但是没有提示 ,代码出现黄色标记以及红色波浪线 ,经验不
- 一、Mock介绍1、什么是Mock模拟接口接口Mock测试:在接口测试中,对于某些不容易构造或者不容易获取的接口,可以用一个模拟接口来代替2
- 1、问题描述:在学习北京大学曹健老师的tensorflow2.0笔记的时候,遇到了[Errno 2] No such file or dir
- golang常用库之-pkg/errors包背景golang自带了错误信息包error只提供了简单的用法, 如errors.New(),和e
- 客户强烈要求使用淘宝的首页商品分类效果,很BT~,没辙就满足一下人家的需求。通过淘宝案例,立即想到了显示/隐藏层的效果,于是在DW中画了几个
- SQL Server 2016真的让人眼前一亮。几天前微软就提供了RCO(候选发布版)版本的下载。我已经围观了一圈RCO版本,其中一个最拽的
- K-近邻算法概述简单地说, k-近邻算法采用测量不同特征值之间的距离方法进行分类。k-近邻算法优点:精度高、对异常值不敏感、无数据输入假定。