Vue + Node.js + MongoDB圖片上傳組件實現(xiàn)圖片預(yù)覽和刪除功能詳解
本文實例講述了Vue + Node.js + MongoDB圖片上傳組件實現(xiàn)圖片預(yù)覽和刪除功能。分享給大家供大家參考,具體如下:
公司要寫一些為自身業(yè)務(wù)量身定制的的組件,要基于Vue,寫完后擴展了一下功能,選擇寫圖片上傳是因為自己之前一直對這個功能比較迷糊,所以這次好好了解了一下。演示在網(wǎng)址打開后的show.gif中。
使用技術(shù):Vue.js | node.js | express | MongoDB。
github網(wǎng)址:https://github.com/neroneroffy/private-project/tree/master/vue_uploader

功能
- 單圖多圖上傳
- 圖片上傳預(yù)覽
- 上傳進度條
- 分組上傳,分組查詢
- 新建分組,刪除分組
- 刪除圖片
- 選擇圖片
目錄結(jié)構(gòu)

前端利用Vue搭建,Entry.vue中引入子組件Upload.vue。在Upload.vue中,使用input標簽,上傳圖片,form表單提交數(shù)據(jù),但是from讓人很頭疼,提交后刷新頁面,所以給它綁定了一個隱藏的iframe標簽來實現(xiàn)無刷新提交表單。
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>
調(diào)用上傳函數(shù)提交完數(shù)據(jù)后:
upload(); document.forms[0].target="rfFrame";
圖片預(yù)覽
利用html5的fileReader對象
let count = 0;//上傳函數(shù)外定義變量,記錄文件的數(shù)量,即遞歸的次數(shù)
/*-----------------------此段代碼在上傳函數(shù)中-------------------------------*/
let fileReader = new FileReader();
//解析圖片路徑,實現(xiàn)預(yù)覽
fileReader.readAsDataURL(file.files[count]);
fileReader.onload=()=>{
previewData = {
url:fileReader.result,//圖片預(yù)覽的img標簽的src
name:file.files[count].name,
size:file.files[count].size,
};
//這段代碼在上傳函數(shù)中,所以在外面定義了一個_this = this,fileList為vue的data中定義的狀態(tài)
_this.fileList.push(previewData);
};
進度條實現(xiàn)
在axios的配置中定義onUploadProgress函數(shù),接收參數(shù):progressEvent,利用它的兩個屬性:progressEvent.total和progressEvent.loaded(上傳的文件總字節(jié)數(shù)和已上傳的字節(jié)數(shù))
node寫接口,實現(xiàn)圖片的接收、查詢、刪除。實現(xiàn)分組的新增、查詢、刪除。利用Formidable模塊接收并處理前端傳過來的表單數(shù)據(jù)。利用fs模塊實現(xiàn)刪除文件功能。
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);
對于上傳方式,我這里統(tǒng)一采用依次上傳,無論是單圖多圖,單圖上傳一次就好,多圖則遞歸調(diào)用上傳函數(shù),直到遞歸次數(shù)等于圖片數(shù)量,停止遞歸。
上傳函數(shù)
let file=$event.target,
formData = new FormData();
//遞歸調(diào)用自身,實現(xiàn)多文件依次上傳
let _this = this;
let count = 0;
let previewData = {};
uploadImage($event){
let file=$event.target,
formData = new FormData();
//遞歸調(diào)用自身,實現(xiàn)多文件依次上傳
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(`進度條的數(shù)量${_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();
//解析圖片路徑,實現(xiàn)預(yù)覽
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{
//發(fā)送數(shù)據(jù)
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{
//如果是選擇其他組上傳,直接把返回數(shù)據(jù)賦值到文件數(shù)組
_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');
});
}
};
}
}
//第一次調(diào)用
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;//以單文件依次上傳的方式,實現(xiàn)多文件上傳 form.maxFieldsSize = 1*1024; //解析圖片,重命名圖片名稱,返回給前端。 let fileData = ""; let fileDir = "images";//定義文件的存放路徑 let route = 'upload_';//定義路由 let serverIp = 'http://localhost:3002/';//定義服務(wù)器IP
對文件數(shù)據(jù)進行處理,存入本地并存入數(shù)據(jù)庫(由于涉及到分組上傳。。。所以比較復(fù)雜)
解析文件函數(shù):
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:'發(fā)生錯誤了'
})
}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
})
}
});
}
}
})
}
}
})
}
最后,調(diào)用解析文件函數(shù)
form.parse(req,(err,fields,files)=>{
//傳多個文件
if(files.file instanceof Array){
return
}else{
//傳單個文件
handleFile(files.file)
}
});
數(shù)據(jù)庫結(jié)構(gòu):

剩下的還有文件刪除,新增分組,刪除分組,分組查詢的功能,由于篇幅有限,這些功能可以去看源碼
第一次用node和mongoDB寫后臺業(yè)務(wù),還有很多地方需要完善,代碼會繼續(xù)更新~
希望本文所述對大家vue.js程序設(shè)計有所幫助。
相關(guān)文章
在 Vue-CLI 中引入 simple-mock實現(xiàn)簡易的 API Mock 接口數(shù)據(jù)模擬
本文以 Vue-CLI 為例介紹引入 simple-mock 實現(xiàn)前端開發(fā)數(shù)據(jù)模擬的步驟。感興趣的朋友跟隨小編一起看看吧2018-11-11
vue3通過render函數(shù)實現(xiàn)菜單下拉框的示例
本文主要介紹了vue3通過render函數(shù)實現(xiàn)菜單下拉框的示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Nuxt如何實現(xiàn)將服務(wù)測數(shù)據(jù)存儲到Vuex中
這篇文章主要介紹了Nuxt如何實現(xiàn)將服務(wù)測數(shù)據(jù)存儲到Vuex中的方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10
element-ui圖像組件、上傳組件以及分頁組件實現(xiàn)代碼
工作中碰到需要多圖上傳,在使用element-ui解決過程中碰到一些問題,在這里分享給大家,這篇文章主要給大家介紹了關(guān)于element-ui圖像組件、上傳組件以及分頁組件實現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-02-02

