1. 先说前端的文件上传
你有没有用过 Element Plus 的 el-upload?
<el-upload
action="/api/upload"
:auto-upload="false"
:on-change="handleChange"
>
<el-button>选择文件</el-button>
</el-upload>
点击按钮 → 选中文件 → 自动上传。
这背后原理是啥?
就是 FormData + axios 发送到后端!
const data = new FormData();
data.set('file', file对象);
axios.post('/api/upload', data);
2. 后端怎么接收?用 multer!
multer 就是 Express 专门用来处理文件上传的中间件。
安装
npm install express multer cors
基础用法
const express = require('express');
const multer = require('multer');
const cors = require('cors');
const app = express();
app.use(cors());
// 配置上传目录
const upload = multer({ dest: 'uploads/' });
// 处理单文件上传
app.post('/upload', upload.single('file'), (req, res) => {
console.log('文件信息:', req.file); // 文件详情
console.log('其他字段:', req.body); // 其他表单字段
res.send('上传成功');
});
app.listen(3333);
关键点:
upload.single('file')- 接收字段名为file的单个文件req.file- 获取上传的文件信息req.body- 获取其他非文件字段
3. 前端怎么传?
<input type="file" id="fileInput" />
<script>
const fileInput = document.querySelector('#fileInput');
fileInput.onchange = async () => {
const data = new FormData();
data.set('file', fileInput.files[0]);
const res = await axios.post('http://localhost:3333/upload', data);
console.log(res.data);
};
</script>
对比 Element Plus el-upload:
// el-upload 内部就是这样干的
const data = new FormData();
data.append('file', file.raw);
axios.post('/api/upload', data);
4. 多文件上传
场景:一次传多个文件
// 后端 - 最多传 2 个文件
app.post('/upload-multiple', upload.array('files', 2), (req, res) => {
console.log('文件列表:', req.files); // 数组形式
res.send('上传成功');
});
前端
<input type="file" id="fileInput" multiple />
fileInput.onchange = async () => {
const data = new FormData();
// 多个文件用 append 追加
[...fileInput.files].forEach(file => {
data.append('files', file);
});
await axios.post('http://localhost:3333/upload-multiple', data);
};
对比 el-upload:
<el-upload
:auto-upload="false"
:file-list="fileList"
multiple
>
</el-upload>
5. 不同字段传不同文件
场景:头像 + 附件分开传
// 后端
app.post('/upload-fields', upload.fields([
{ name: 'avatar', maxCount: 1 }, // 头像,最多1个
{ name: 'attachments', maxCount: 3 } // 附件,最多3个
]), (req, res) => {
console.log('头像:', req.files['avatar']);
console.log('附件:', req.files['attachments']);
res.send('上传成功');
});
前端
const data = new FormData();
data.append('avatar', avatarFile); // 头像
data.append('attachments', file1); // 附件1
data.append('attachments', file2); // 附件2
await axios.post('http://localhost:3333/upload-fields', data);
对比 el-upload:
<el-upload
action="/api/upload"
:auto-upload="false"
:file-list="fileList"
>
<el-button>上传头像和附件</el-button>
</el-upload>
6. 错误处理
传的文件超过限制怎么办?
app.post('/upload-multiple', upload.array('files', 2),
(req, res) => {
res.send('上传成功');
},
// 错误处理中间件
(err, req, res, next) => {
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') {
res.status(400).send('文件太大');
} else if (err.code === 'LIMIT_UNEXPECTED_FILE') {
res.status(400).send('文件数量超限');
}
} else {
res.status(500).send('服务器错误');
}
}
);
7. 自定义文件名和保存路径
默认会生成随机文件名,想自定义?
const path = require('path');
const storage = multer.diskStorage({
// 保存目录
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
// 文件名
filename: (req, file, cb) => {
const ext = path.extname(file.originalname); // 扩展名
const name = Date.now() + '-' + Math.random().toString(36).substr(2, 9);
cb(null, name + ext);
}
});
const upload = multer({ storage });
对比前端的 URL.createObjectURL:
// 前端预览本地图片
const url = URL.createObjectURL(file);
img.src = url;
8. 完整流程图
前端 后端
─── ───
FormData + axios ──────────► Express + multer
│ │
│ multipart/form-data │
│ (文件分割传输格式) │
│ │
│ req.files
│ ▼
│ 保存到磁盘
│ │
◄────────────────────────────────────┘
响应
9. 总结
| multer 方法 | 作用 | 对比前端 |
|---|---|---|
upload.single('field') | 单文件上传 | el-upload 单文件 |
upload.array('field', n) | 多文件上传 | el-upload multiple |
upload.fields([...]) | 多字段多文件 | - |
upload.any() | 任意文件 | - |
multer.diskStorage() | 自定义存储 | URL.createObjectURL |
一句话总结:
multer 就是 Express 的文件上传中间件,类似于前端的 el-upload 组件帮你搞定文件上传。
后端接收文件 = multer 前端上传文件 = FormData + axios / el-upload
看完还不懂?评论区见!
觉得有帮助,点个赞再走~ 👋