上传文件是前端常见的功能,比如说常见的场景有,登录用户的自定义用户头像,写博客或者新闻的列表封面图,商城系统的商品图等,考试系统上传试卷等。可以说上传文是每个前端开发者必备的技能。
本文将从全栈角度,使用 Node.js + Express 构建后端服务,搭配 Vue 3 + Element Plus 实现前端上传组件,涵盖以下核心内容:
- 结构设计
- 后端跨域配置
- express 路由接口设计
- multer 设置上传目录
- multer 设置显示上单个文件大小
- multer 接收多个文件和单个文件的方式
- multer 文件命名
- element-plus 中的el-upload的使用
- el-upload 配置上传多个
- el-upload 配置缩略图模式
- el-upload 配置拖拽上传
- el-upload 配置手动上传
一、 结构设计
后端结构
api/ # 项目根目录(Node.js + Express 后端)
├── src/ # 后端核心源码目录(保留您的原始结构)
│ ├── config/ # 配置文件目录
│ │ └── upload.js # 上传配置(大小,个数等)
│ ├── controller/ # 业务逻辑控制器
│ │ └── UploadController.js # 处理文件上传的核心逻辑
│ ├── routes/ # 路由定义
│ │ └── index.js # 接口路由配置
│ ├── utils/ # 工具函数
│ │ ├── upload.js # 上传相关工具(校验/处理)
│ └── uploads/ # 文件存储目录
├── node_modules/ # Node.js 依赖库
├── package.json # 项目配置和脚本
└── package-lock.json # 依赖版本锁定文件
前端结构
web/ # 项目根目录(Vue3 + Vite 前端项目)
├── .vscode/ # VSCode 编辑器配置(如调试设置)
├── node_modules/ # 项目依赖库(npm/pnpm/yarn 安装)
├── public/ # 静态资源目录(直接复制到构建输出)
│ └── favicon.ico # 网站图标等(不会被 Vite 处理)
├── src/ # 核心源代码目录
│ ├── assets/ # 静态资源(CSS/图片/字体等,会被 Vite 处理)
│ ├── components/ # 可复用 Vue 组件
│ │ └── upload/ # 文件上传相关组件
│ │ └── index.vue # 上传组件主逻辑(Element Plus 集成)
│ ├── App.vue # Vue 根组件
│ └── main.js # 应用入口文件
├── .gitignore # Git 忽略规则(排除 node_modules 等)
├── index.html # 主 HTML 入口(Vite 注入脚本)
├── package.json # 项目配置和依赖声明
├── pnpm-lock.yaml # 依赖版本锁定文件(pnpm 专用)
├── README.md # 项目说明文档
└── vite.config.js # Vite 构建配置(代理、插件等)
二、后端开发
1. 安装开发依赖
npm i express multer cors
2. src/index.js
const path = require("path");
const express = require("express");
const cors = require("cors");
const router = require("./routes");
const corsConfig = require("./config/cors");
const app = express();
// app.use(cors());
app.use(cors(corsConfig)); // 传入特定的配置
app.use("/uploads", express.static(path.resolve(process.cwd(), "uploads")));
app.use("/api", router);
app.listen(3000, () => {
console.log("http://localhost:3000");
});
可以看到,在入口文件中:
- 引入了express 来快速搭建后端服务
- 引入了cors 配置来进行跨域的配置,可以进行源的配置,允许的请求方式的配置,允许的请求头配置,什么参数都不传的代表可以允许所有源,所有请求方式,所有请求头
- 引入了外部的router, 并添加/api前缀
3. src/routes/index.js
const express = require("express");
const { fileCount } = require("../config/upload");
const { uploadController } = require("../controller/UploadController");
const { upload } = require("../utils/upload");
const router = express.Router();
router.post("/upload", upload.array("file", fileCount), uploadController);
module.exports = router;
在路由文件中:
- 通过express.Router() 创建的路由系统进行请求的处理
- upload.array("file", fileCount) 是multer 相关的配置,file 上传对应的字段名,fileCount 一次只能上传多少个,array是多个文件的时候,如果只是单个文件上传可以使用upload.array("single")
- uploadController 处理请求响应相关逻辑
4. src/controller/UploadController.js
const upload = (req, res) => {
console.log(req.files);
if (!req.files?.length) {
return res.json({
code: 404,
message: "未选择文件",
});
}
// 将上传的文件数据返回给前端
const uploads = req.files.map((file) => ({
originalname: file.originalname,
filename: file.filename,
size: file.size,
url: `http://localhost:3000/uploads/${file.filename}`,
}));
res.json({
code: 200,
message: "success",
data: uploads,
});
};
module.exports.uploadController = upload;
在UploadController 中:
- 首先判断是否选择了文件,如果没有则直接返回未选择
- 如果选择了文件 则处理数据格式通过data 字段将上传的数据信息返回给前端
5. src/utils/upload.js
const path = require("path");
const fs = require("fs");
const multer = require("multer");
const { fileSize } = require("../config/upload");
// 配置上传目录
const uploadDir = path.join(process.cwd(), "uploads");
// 如果目录不存在则创建一个对应的目录
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir);
// Multer 配置,限制文件大小,存储规则
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, uploadDir),
filename: (req, file, cb) => {
const uniqueName = `${Date.now()}-${Math.random().toString(36).slice(2)}-${
file.originalname
}`;
cb(null, uniqueName);
},
});
const upload = multer({
storage,
limits: { fileSize },
});
module.exports.upload = upload;
在这个工具函数中:
- 通过 multer.diskStorage 方法设置了上传的文件路径,上传的文件名
- 当上传的文件夹不存在时,会自创建一个文件夹
- 通过multer 方法的执行传的参数中的limits属性的fileSize 设置了单个文件的大小限制
6. src/config/cors
module.exports = {
origin: "http://localhost:5173",
methods: "GET,POST,PUT,DELETE",
allowedHeaders: ["Content-type"],
};
在这个配置中主要配置了允许跨域请求的源,允许请求的方法,允许请求的请求头
7. src/config/upload.js
const fileSize = 1024 * 1024 * 10; // 限制每个文件上传的大小
const fileCount = 10; // 限制一次只能上传多少个文件
module.exports = {
fileSize,
fileCount,
};
7. packages.json中添加启动命令
"scripts": {
"dev": "nodemon ./src/index.js"
},
执行 npm run dev 启动项目
三、前端开发
1. 项目初始化和依赖安装
pnpm create vite web --template vue // 项目初始化
cd web // 切换到项目根目录
pnpm i element-plus
pnpm i unplugin-element-plus -D // element-plus手动引入时需要的vite插件
pnpm run dev // 启动项目
2. vite.config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
import ElementPlus from "unplugin-element-plus/vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), ElementPlus()],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"), // 配置别名
},
},
});
在这里面:
- 添加了别名配置
- 添加 unplugin-element-plus/vite 插件
3. App.vue
<template>
<div>
<upload />
</div>
</template>
<script setup>
import upload from "@/components/upload/index.vue";
</script>
<style scoped></style>
4. src/compoents/upload/index.vue
<template>
<div>
<ElUpload
action="http://localhost:3000/api/upload"
v-model:file-list="fileList"
method="post"
>
<el-button type="primary">上传文件</el-button>
</ElUpload>
</div>
</template>
<script setup>
import "element-plus/es/components/upload/style/css";
import { ref } from "vue";
import { ElUpload, ElButton } from "element-plus";
const fileList = ref([]);
</script>
<style scoped></style>
- action 请求服务端的地址
- 选择的文件列表
- method 定义请求方式
演示效果:
查看api下面的uploads 就会发现多了刚才上传的文件
在浏览器的网络模块也能看到正常的返回值:
这样就完成了文件的上传功能。
5. 配置缩略图预览模式
安装element-plus 图标库
pnpm install @element-plus/icons-vue
在ElUpload组件上增加 list-type="picture-card", 导入相关组件:
import { ElUpload, ElIcon } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
将模板中的
<el-button type="primary">上传文件</el-button>
修改成
<el-icon><Plus /></el-icon>
查看运行效果
没有选择图片上传之前
选择图片上传之后
6. 配置拖拽上传模式
在pc端的产品中,很多用户喜欢拖拽上传的方式,使用ElUpload实现拖拽上传也非常简单,只需要添加drag属性即可
7. 配置手动上传
ElUpload 组件默认选择完文件后就开始上传,但是有时候,我们需要选择完之后,看下效果,点击确定在上传。ElUpload 组件实现手动上传需要做如下修改:
- 增加ref="uploadRef" 用来手动提价
- :auto-upload="false" 阻止选择完成之后上传
- 将
<ElIcon><Plus></Plus></ElIcon>
移动到 template 中,并且template 上添加#trigger, 这是命名slot 的简写形式 - 添加手动提交的按钮
<ElButton type="success" class="click-upload" @click="submitUpload"
>点击上传</ElButton
>
js 中,添加手动处理逻辑:
const uploadRef = ref();
const submitUpload = () => {
uploadRef.value.submit();
};
来看下效果:
可以看到,我们选择文件的时候,并没向后端发送请求,点击了点击上传按钮之后才发送请求的。这样就实现了手动上传。
7. 配置一次可以选择多个文件进行上传
ElUpload 实现选择多个文件上传,其实也很简单,只需要添加组件上添加multiple 属性即可。
四、总结
本篇主要分享了文件上传的整个过程,后端使用Node, Express,multer实现上传功能,前端使用Vue3,Element Plus 实现前端的交互的,并详细介绍了常用的上传模式配置。
感谢你的收看,若您对Vue3开发感兴趣,可以关注Vue 知识储备专栏