【VUE案例练习】前端vue2+element-ui,后端nodo+express实现‘‘文件上传/删除‘‘功能

近期在做跟毕业设计相关的数据后台管理系统,其中的列表项展示有图片展示,添加/编辑功能有文件上传。

“文件上传/删除”也是我们平时开发会遇到的一个功能,这里分享个人的实现过程,与大家交流谈论~

一、准备工作

  • 本次案例使用的node版本是18.16.1
  • 前端vue2+element-ui
  • 后端使用node+express
  • 安装对应库:element-ui、axios、multer

注意:本篇重点介绍文件上传的相关功能,后端express的具体使用可以看我的express专栏~

express专栏

二、前端

文件上传组件,来自element-ui组件库

注意:自行安装组件库

Element - The world's most popular Vue UI framework

关于组件,有些展示和功能需要对应设置(比如文件上传的服务器地址,删除后端文件等等)才可以正常使用,所以我对代码进行了修改补充。

组件代码

<template><div><el-uploadref="upload"action="http://localhost:4000/api/upload"  //文件上传的接口list-type="picture-card":on-preview="handlePictureCardPreview":on-remove="handleRemove":file-list="fileList":on-change="handleFileChange":on-success="handleUploadSuccess":on-error="handleUploadError"name="file"><i class="el-icon-plus" /></el-upload><el-dialog :visible.sync="dialogVisible"><img width="100%" :src="dialogImageUrl" alt=""></el-dialog></div>
</template>

 变量和相关函数

注意:需要安装axios(若安装则跳过)

npm install axios
<script>
import Axios from 'axios'
export default {data() {return {dialogImageUrl: '', //预览时展示的图片dialogVisible: false,fileList: [] // 用于存储文件列表}},methods: {// 生成文件预览 URLhandleFileChange(file, fileList) {file.url = URL.createObjectURL(file.raw)this.fileList = fileList},// 删除文件handleRemove(file, fileList) {this.fileList = fileList// 调用后端删除接口if (file.response && file.response.data) {this.deleteFile(file.response.data)} else {this.$message.warning('文件尚未上传成功,无法删除')}},// 预览图片handlePictureCardPreview(file) {this.dialogImageUrl = file.urlthis.dialogVisible = true},// 文件上传成功handleUploadSuccess(response, file) {console.log('文件上传成功', response)if (response.code === 0) {// 从后端响应中获取图片的 URLconst imageUrl = response.data// 更新 fileList 中的文件 URLconst index = this.fileList.findIndex(f => f.uid === file.uid)if (index !== -1) {this.fileList[index].url = imageUrlthis.fileList[index].response = response // 保存后端返回的响应}this.$message.success('文件上传成功')} else {this.$message.error('文件上传失败: ' + response.msg)}},// 文件上传失败handleUploadError(err, file) {console.error('文件上传失败', err)this.$message.error('文件上传失败')},deleteFile(filename) {// 去掉前缀 /public/static/upload/const pureFilename = filename.replace('/public/static/upload/', '')// 调用后端删除接口Axios.delete(`http://localhost:4000/api/upload/${pureFilename}`).then(response => {if (response.data.code === 0) {this.$message.success('文件删除成功')} else {this.$message.error('文件删除失败: ' + response.data.msg)}}).catch(err => {console.error('文件删除失败', err)this.$message.error('文件删除失败')})}}
}
</script>

完整代码

 注意看:''localhost:4000''相关字眼(关于后端接口的,下文会作介绍)

<template><div><el-uploadref="upload"action="http://localhost:4000/api/upload"list-type="picture-card":on-preview="handlePictureCardPreview":on-remove="handleRemove":file-list="fileList":on-change="handleFileChange":on-success="handleUploadSuccess":on-error="handleUploadError"name="file"><i class="el-icon-plus" /></el-upload><el-dialog :visible.sync="dialogVisible"><img width="100%" :src="dialogImageUrl" alt=""></el-dialog></div>
</template><script>
import Axios from 'axios'
export default {data() {return {dialogImageUrl: '',dialogVisible: false,fileList: [] // 用于存储文件列表}},methods: {// 生成文件预览 URLhandleFileChange(file, fileList) {file.url = URL.createObjectURL(file.raw)this.fileList = fileList},// 删除文件handleRemove(file, fileList) {this.fileList = fileList// 调用后端删除接口if (file.response && file.response.data) {this.deleteFile(file.response.data)} else {this.$message.warning('文件尚未上传成功,无法删除')}},// 预览图片handlePictureCardPreview(file) {this.dialogImageUrl = file.urlthis.dialogVisible = true},// 文件上传成功handleUploadSuccess(response, file) {console.log('文件上传成功', response)if (response.code === 0) {// 从后端响应中获取图片的 URLconst imageUrl = response.data// 更新 fileList 中的文件 URLconst index = this.fileList.findIndex(f => f.uid === file.uid)if (index !== -1) {this.fileList[index].url = imageUrlthis.fileList[index].response = response // 保存后端返回的响应}this.$message.success('文件上传成功')} else {this.$message.error('文件上传失败: ' + response.msg)}},// 文件上传失败handleUploadError(err, file) {console.error('文件上传失败', err)this.$message.error('文件上传失败')},// 删除后端文件,参数传递的是文件名(经前端上传过后,返回给前端的文件名)deleteFile(filename) {// 去掉前缀 /public/static/upload/const pureFilename = filename.replace('/public/static/upload/', '')// 调用后端删除接口Axios.delete(`http://localhost:4000/api/upload/${pureFilename}`).then(response => {if (response.data.code === 0) {this.$message.success('文件删除成功')} else {this.$message.error('文件删除失败: ' + response.data.msg)}}).catch(err => {console.error('文件删除失败', err)this.$message.error('文件删除失败')})}}
}
</script><style>
.el-upload-list__item-thumbnail {width: 100%;height: 100%;object-fit: cover;
}
</style>

三、后端

1、搭建express应用

【express-generator】01-安装和基本使用

如果按照文章步骤(默认端口是3000,我这里设置成4000端口),则对应更换端口为4000,在创建的express项目的bin/www中进行修改。

2、新建文件夹以及文件

2.1、新建public/static/upload

这是存储上传文件的位置。

 2.2、新建routes/upload.js,用于撰写路由方法

安装multer,这是处理文件上传的相关库。

npm install multer

var express = require("express");
const multer = require("multer");
const { uploading } = require("../utils/tool");
const fs = require('fs');
const path = require('path');
var router = express.Router();// 文件上传接口
router.post("/", async function (req, res, next) {// single 方法里面书写上传控件的 name 值uploading.single("file")(req, res, function (err) {if (err instanceof multer.MulterError) {next("上传文件失败,请检查文件的大小,控制在 6MB 以内");} else {console.log("req.file>>>", req.file);const filePath = "/public/static/upload/" + req.file.filename;res.send({ code: 0, msg: "文件上传成功", data: filePath });}})
});// 文件删除接口
router.delete("/:filename", function (req, res, next) {const filename = req.params.filename;const filePath = path.join(__dirname, '../public/static/upload',filename);console.log("后端接收到的文件参数>>>",filename,"完整基本路径是>>>>",filePath);fs.unlink(filePath, (err) => {if (err) {console.error("删除文件失败", err);return res.status(500).send({ code: 1, msg: "删除文件失败" });}res.send({ code: 0, msg: "文件删除成功" });});
});module.exports = router;

2.3、新增utlis/tool.js,撰写工具类函数

const multer = require("multer");
const path = require("path");// 设置上传文件的引擎
const storage = multer.diskStorage({// 文件存储的位置destination: function (req, file, cb) {cb(null, __dirname + '/../public/static/upload');},// 上传到服务器的文件,文件名要做单独处理filename: function (req, file, cb) {// 获取文件名const basename = path.basename(file.originalname, path.extname(file.originalname));// 获取后缀名const extname = path.extname(file.originalname);// 构建新的名字const newName = basename + new Date().getTime() + Math.floor(Math.random() * 9000 + 1000) + extname;cb(null, newName);}
})module.exports.uploading = multer({storage: storage,limits: {fileSize: 6000000,files: 1}
})

 3、在app.js中引入和使用

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const cors = require('cors');
// 文件上传
const multer = require('multer');
const fs = require('fs');
// const path = require('path');var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var uploadRouter = require('./routes/upload');
var app = express();// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');// 允许所有来源访问
app.use(cors());// 文件上传
// const upload = multer({ dest: 'static/upload/' });
app.use(express.static(path.join(__dirname, 'public')));
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/api/upload/', uploadRouter);// error handler
app.use(function(err, req, res, next) {// set locals, only providing error in developmentres.locals.message = err.message;res.locals.error = req.app.get('env') === 'development' ? err : {};// render the error pageres.status(err.status || 500);res.render('error');
});module.exports = app;

四、测试

1、上传文件(图片)

 查看存储上传文件的位置

 2、删除文件(图片)

前端组件,鼠标进入到图片区域,点击删除按键

前端作出对应提示

最后前端的文件列表也为空,成功删除了文件。

后端查看文件夹,发现刚上传的文件由于前端的删除操作,也对应进行了删除。

 

 五、总结

一些注意点

express应用默认端口号是3000,而案例演示的是4000(因为一般情况,3000端口容易被其他代码程序给使用用,避免这种情况,可以使用一个新的端口号(或者把占用3000端口的程序都关闭))

关于文件上传的设置,涉及到的知识点比较多,比如fs.unlink,path相关的知识,需要大家自行进行补充了解,部分知识点可以参考下方这篇博客文章。

【NODE】01-fs和path常用知识点


如果有问题,请留言评论~

如果你喜欢这篇文章,留下你的点赞和收藏~ 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/67678.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++中的析构器(Destructor)(也称为析构函数)

在C中&#xff0c;析构器&#xff08;Destructor&#xff09;也称为析构函数&#xff0c;它是一种特殊的成员函数&#xff0c;用于在对象销毁时进行资源清理工作。以下是关于C析构器的详细介绍&#xff1a; 析构函数的特点 名称与类名相同&#xff0c;但前面有一个波浪号 ~&a…

VLN视觉语言导航基础

0 概述 视觉语言导航模型旨在构建导航决策模型 π π π&#xff0c;在 t t t时刻&#xff0c;模型能够根据指令 W W W、历史轨迹 τ { V 1 , V 2 , . . . , V t − 1 } \tau\{V_1,V_2,...,V_{t-1}\} τ{V1​,V2​,...,Vt−1​}和当前观察 V t { P t , R t , N ( V t ) } V_…

AI协助探索AI新构型的自动化创新概念

训练AI自生成输出模块化代码&#xff0c;生成元代码级别的AI功能单元代码&#xff0c;然后再由AI组织为另一个AI&#xff0c;实现AI开发AI的能力&#xff1b;用AI协助探索迭代新构型AI将会出现&#xff0c;并成为一种新的技术路线潮流。 有限结点&#xff0c;无限的连接形式&a…

Flux的三步炼丹炉——fluxgym(三):矩阵测试

前面两篇文章给大家介绍了如何准备素材和怎么炼丹&#xff0c;现在我们拿到训练完成后的多个Lora怎么才能确定哪个才是我们需要的、效果最好的呢&#xff1f;答案就是使用xyz图表测试&#xff0c;也称为矩阵测试&#xff0c;通过控制控制变量的方法对Lora模型批量生图&#xff…

利用Muduo库实现简单且健壮的Echo服务器

一、muduo网络库主要提供了两个类&#xff1a; TcpServer&#xff1a;用于编写服务器程序 TcpClient&#xff1a;用于编写客户端程序 二、三个重要的链接库&#xff1a; libmuduo_net、libmuduo_base、libpthread 三、muduo库底层就是epoll线程池&#xff0c;其好处是…

文件读写操作

写入文本文件 #include <iostream> #include <fstream>//ofstream类需要包含的头文件 using namespace std;void test01() {//1、包含头文件 fstream//2、创建流对象ofstream fout;/*3、指定打开方式&#xff1a;1.ios::out、ios::trunc 清除文件内容后打开2.ios:…

C++编程语言:抽象机制:模板(Bjarne Stroustrup)

目录 23.1 引言和概观(Introduction and Overview) 23.2 一个简单的字符串模板(A Simple String Template) 23.2.1 模板的定义(Defining a Template) 23.2.2 模板实例化(Template Instantiation) 23.3 类型检查(Type Checking) 23.3.1 类型等价(Type Equivalence) …

定制Centos镜像(二)

本章是对上篇文章的扩展&#xff1a; https://blog.csdn.net/qq_50247813/article/details/145286244 上篇文章学习了如何自定义镜像安装。这篇介绍如何在定制镜像的时候安装其他软件&#xff1b; &#xff08;源文件参考上篇文章&#xff09; 根据上篇文章的步骤&#xff0c;…

【机器学习理论】朴素贝叶斯网络

基础知识&#xff1a; 先验概率&#xff1a;对某个事件发生的概率的估计。可以是基于历史数据的估计&#xff0c;可以由专家知识得出等等。一般是单独事件概率。 后验概率&#xff1a;指某件事已经发生&#xff0c;计算事情发生是由某个因素引起的概率。一般是一个条件概率。 …

Flutter 新春第一弹,Dart 宏功能推进暂停,后续专注定制数据处理支持

在去年春节&#xff0c;Flutter 官方发布了宏&#xff08;Macros&#xff09;编程的原型支持&#xff0c; 同年的 5 月份在 Google I/O 发布的 Dart 3.4 宣布了宏的实验性支持&#xff0c;但是对于 Dart 内部来说&#xff0c;从启动宏编程实验开始已经过去了几年&#xff0c;但…

计算机组成原理——存储系统(一)

在人生的道路上&#xff0c;成功与失败交织成一幅丰富多彩的画卷。不论我们是面对胜利的喜悦&#xff0c;还是遭遇失败的痛苦&#xff0c;都不能放弃对梦想的追求。正是在这种追求中&#xff0c;我们不断地超越自我&#xff0c;不断地突破自己的极限。只有勇往直前&#xff0c;…

前端知识速记:节流与防抖

前端知识速记&#xff1a;节流与防抖 什么是防抖&#xff1f; 防抖是一种控制事件触发频率的方法&#xff0c;通常用于处理用户频繁触发事件的场景。防抖的核心思想是将多个连续触发事件合并为一个事件&#xff0c;以减少执行次数。它在以下场景中特别有效&#xff1a; 输入…

无人机图传模块 wfb-ng openipc-fpv,4G

openipc 的定位是为各种模块提供底层的驱动和linux最小系统&#xff0c;openipc 是采用buildroot系统编译而成&#xff0c;因此二次开发能力有点麻烦。为啥openipc 会用于无人机图传呢&#xff1f;因为openipc可以将现有的网络摄像头ip-camera模块直接利用起来&#xff0c;从而…

蓝桥杯例题一

不管遇到多大的困难&#xff0c;我们都要坚持下去。每一次挫折都是我们成长的机会&#xff0c;每一次失败都是我们前进的动力。路漫漫其修远兮&#xff0c;吾将上下而求索。只有不断努力奋斗&#xff0c;才能追逐到自己的梦想。不要害怕失败&#xff0c;害怕的是不敢去尝试。只…

【JavaEE进阶】图书管理系统 - 壹

目录 &#x1f332;序言 &#x1f334;前端代码的引入 &#x1f38b;约定前后端交互接口 &#x1f6a9;接口定义 &#x1f343;后端服务器代码实现 &#x1f6a9;登录接口 &#x1f6a9;图书列表接口 &#x1f384;前端代码实现 &#x1f6a9;登录页面 &#x1f6a9;…

【算法设计与分析】实验8:分支限界—TSP问题

目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 掌握分支界限求解问题的思想&#xff1b;针对不同的问题&#xff0c;能够利用分支界限法进行问题拆分和求解以及时间复杂度分析…

【3】阿里面试题整理

[1]. ES架构&#xff0c;如何进行路由以及选主 路由&#xff1a;在Elasticsearch&#xff08;ES&#xff09;中&#xff0c;默认的路由算法是基于文档的_id。具体来说&#xff0c;Elasticsearch会对文档的_id进行哈希计算&#xff0c;然后对分片数量取模&#xff0c;以确定该文…

【Linux】opencv在arm64上提示找不到libjasper-dev

解决opencv在arm64上提示找不到libjasper-dev的问题。 本文首发于❄慕雪的寒舍 问题说明 最近我在尝试编译opencv&#xff0c;安装依赖项libjasper1和libjasper-dev的时候就遇到了这个问题。在amd64平台上&#xff0c;我们可以通过下面的命令安装&#xff08;ubuntu18.04&…

【数据结构】_时间复杂度相关OJ(力扣版)

目录 1. 示例1&#xff1a;消失的数字 思路1&#xff1a;等差求和 思路2&#xff1a;异或运算 思路3&#xff1a;排序&#xff0b;二分查找 2. 示例2&#xff1a;轮转数组 思路1&#xff1a;逐次轮转 思路2&#xff1a;三段逆置&#xff08;经典解法&#xff09; 思路3…

基于微信小程序的电子商城购物系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…