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

news2025/2/3 11:22:35

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

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

一、准备工作

  • 本次案例使用的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-upload
      ref="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: {
    // 生成文件预览 URL
    handleFileChange(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.url
      this.dialogVisible = true
    },
    // 文件上传成功
    handleUploadSuccess(response, file) {
      console.log('文件上传成功', response)
      if (response.code === 0) {
        // 从后端响应中获取图片的 URL
        const imageUrl = response.data
        // 更新 fileList 中的文件 URL
        const index = this.fileList.findIndex(f => f.uid === file.uid)
        if (index !== -1) {
          this.fileList[index].url = imageUrl
          this.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-upload
      ref="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: {
    // 生成文件预览 URL
    handleFileChange(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.url
      this.dialogVisible = true
    },
    // 文件上传成功
    handleUploadSuccess(response, file) {
      console.log('文件上传成功', response)
      if (response.code === 0) {
        // 从后端响应中获取图片的 URL
        const imageUrl = response.data
        // 更新 fileList 中的文件 URL
        const index = this.fileList.findIndex(f => f.uid === file.uid)
        if (index !== -1) {
          this.fileList[index].url = imageUrl
          this.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 development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.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.coloradmin.cn/o/2291236.html

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

相关文章

使用真实 Elasticsearch 进行高级集成测试

作者&#xff1a;来自 Elastic Piotr Przybyl 掌握高级 Elasticsearch 集成测试&#xff1a;更快、更智能、更优化。 在上一篇关于集成测试的文章中&#xff0c;我们介绍了如何通过改变数据初始化策略来缩短依赖于真实 Elasticsearch 的集成测试的执行时间。在本期中&#xff0…

【R语言】函数

一、函数格式 如下所示&#xff1a; hello&#xff1a;函数名&#xff1b;function&#xff1a;定义的R对象是函数而不是其它变量&#xff1b;()&#xff1a;函数的输入参数&#xff0c;可以为空&#xff0c;也可以包含参数&#xff1b;{}&#xff1a;函数体&#xff0c;如果…

VSCode插件Live Server

简介&#xff1a;插件Live Server能够实现当我们在VSCode编辑器里修改 HTML、CSS 或者 JavaScript 文件时&#xff0c;它都能自动实时地刷新浏览器页面&#xff0c;让我们实时看到代码变化的效果。再也不用手动刷新浏览器了&#xff0c;节省了大量的开发过程耗时&#xff01; 1…

50. 正点原子官方系统镜像烧写实验

一、Windows下使用OTG烧写系统 1、在Windos使用NXP提供的mfgtool来向开发烧写系统。需要用先将开发板的USB_OTG接口连接到电脑上。 Mfgtool工具是向板子先下载一个Linux系统&#xff0c;然后通过这个系统来完成烧写工作。 切记&#xff01;使用OTG烧写的时候要先把SD卡拔出来&…

扩散模型(三)

相关阅读&#xff1a; 扩散模型&#xff08;一&#xff09; 扩散模型&#xff08;二&#xff09; Latent Variable Space 潜在扩散模型&#xff08;LDM&#xff1b;龙巴赫、布拉特曼等人&#xff0c;2022 年&#xff09;在潜在空间而非像素空间中运行扩散过程&#xff0c;这…

it基础使用--5---git远程仓库

it基础使用–5—git远程仓库 1. 按顺序看 -git基础使用–1–版本控制的基本概念 -git基础使用–2–gti的基本概念 -git基础使用–3—安装和基本使用 -git基础使用–4—git分支和使用 2. 什么是远程仓库 在第一篇文章中&#xff0c;我们已经讲过了远程仓库&#xff0c;每个本…

Baklib如何改变内容管理平台的未来推动创新与效率提升

内容概要 在信息爆炸的时代&#xff0c;内容管理平台成为了企业和个人不可或缺的工具。它通过高效组织、存储和发布内容&#xff0c;帮助用户有效地管理信息流。随着技术的发展&#xff0c;传统的内容管理平台逐渐暴露出灵活性不足、易用性差等局限性&#xff0c;这促使市场需…

16.[前端开发]Day16-HTML+CSS阶段练习(网易云音乐五)

完整代码 网易云-main-left-rank&#xff08;排行榜&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name&q…

ARM嵌入式学习--第十天(UART)

--UART介绍 UART(Universal Asynchonous Receiver and Transmitter)通用异步接收器&#xff0c;是一种通用串行数据总线&#xff0c;用于异步通信。该总线双向通信&#xff0c;可以实现全双工传输和接收。在嵌入式设计中&#xff0c;UART用来与PC进行通信&#xff0c;包括与监控…

Unity游戏(Assault空对地打击)开发(3) 摄像机的控制

详细步骤 打开My Assets或者Package Manager。 选择Unity Registry。 搜索Cinemachine&#xff0c;找到 Cinemachine包&#xff0c;点击 Install按钮进行安装。 关闭窗口&#xff0c;新建一个FreeLook Camera&#xff0c;如下。 接着新建一个对象Pos&#xff0c;拖到Player下面…

【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(三)

目录 1 -> 生命周期 1.1 -> 应用生命周期 1.2 -> 页面生命周期 2 -> 资源限定与访问 2.1 -> 资源限定词 2.2 -> 资源限定词的命名要求 2.3 -> 限定词与设备状态的匹配规则 2.4 -> 引用JS模块内resources资源 3 -> 多语言支持 3.1 -> 定…

小程序-基础加强-自定义组件

前言 这次讲自定义组件 1. 准备今天要用到的项目 2. 初步创建并使用自定义组件 这样就成功在home中引入了test组件 在json中引用了这个组件才能用这个组件 现在我们来实现全局引用组件 在app.json这样使用就可以了 3. 自定义组件的样式 发现页面里面的文本和组件里面的文…

尝试ai生成figma设计

当听到用ai 自动生成figma设计时&#xff0c;不免好奇这个是如何实现的。在查阅了不少资料后&#xff0c;有了一些想法。参考了&#xff1a;在figma上使用脚本自动生成色谱 这篇文章提供的主要思路是&#xff1a;可以通过脚本的方式构建figma设计。如果我们使用ai 生成figma脚本…

【周易哲学】生辰八字入门讲解(八)

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文讲解【周易哲学】生辰八字入门讲解&#xff0c;期待与你一同探索、学习、进步&#xff0c;一起卷起来叭&#xff01; 目录 一、六亲女命六亲星六亲宫位相互关系 男命六亲星…

解决whisper 本地运行时GPU 利用率不高的问题

我在windows 环境下本地运行whisper 模型&#xff0c;使用的是nivdia RTX4070 显卡&#xff0c;结果发现GPU 的利用率只有2% 。使用 import torch print(torch.cuda.is_available()) 返回TRUE。表示我的cuda 是可用的。 最后在github 的下列网页上找到了问题 极低的 GPU 利…

【自开发工具介绍】SQLSERVER的ImpDp和ExpDp工具02

工具运行前的环境准备 1、登录用户管理员权限确认 工具使用的登录用户(-u后面的用户)&#xff0c;必须具有管理员的权限&#xff0c;因为需要读取系统表 例&#xff1a;Export.bat -s 10.48.111.12 -d db1 -u test -p test -schema dbo      2、Powershell的安全策略确认…

java异常处理——try catch finally

单个异常处理 1.当try里的代码发生了catch里指定类型的异常之后&#xff0c;才会执行catch里的代码&#xff0c;程序正常执行到结尾 2.如果try里的代码发生了非catch指定类型的异常&#xff0c;则会强制停止程序&#xff0c;报错 3.finally修饰的代码一定会执行&#xff0c;除…

DeepSeek-R1:通过强化学习激励大型语言模型(LLMs)的推理能力

摘要 我们推出了第一代推理模型&#xff1a;DeepSeek-R1-Zero和DeepSeek-R1。DeepSeek-R1-Zero是一个未经监督微调&#xff08;SFT&#xff09;作为初步步骤&#xff0c;而是通过大规模强化学习&#xff08;RL&#xff09;训练的模型&#xff0c;展现出卓越的推理能力。通过强…

低成本、高附加值,具有较强的可扩展性和流通便利性的行业

目录 虚拟资源类 1. 网课教程 2. 设计素材 3. 软件工具 服务类 1. 写作服务 2. 咨询顾问 3. 在线教育 4. 社交媒体管理 虚拟资源类 1. 网课教程 特点&#xff1a;高附加值&#xff0c;可复制性强&#xff0c;市场需求大。 执行流程&#xff1a; 选择领域&#xff1a…

vue入门到实战 二

目录 2.1 计算属性computed 2.1.1什么是计算属性 2.1.2 只有getter方法的计算属性 2.1.3 定义有getter和setter方法的计算属性 2.1.4 计算属性和methods的对比 2.2 监听器属性watch 2.2.1 watch属性的用法 2.2.2 computed属性和watch属性的对比 2.1 计算属性computed…