使用multer上传文件时如何跳过已存在于磁盘的重复文件?
问题原因
你代码的核心问题是中间件执行顺序错误:
multer的upload.array('imgCollection')是路由前置中间件,请求到达/upload接口时,会先执行multer逻辑把所有文件写入磁盘覆盖同名文件,全部写入完成后才会进入你写的MongoDB校验逻辑,此时旧文件已经被覆盖,校验自然失去意义。
修复方案
方案1:使用multer内置fileFilter做前置校验(推荐)
multer提供fileFilter配置项,支持在文件写入磁盘之前做自定义逻辑拦截,你可以在该环节先查询MongoDB判断文件是否存在,存在则直接拒绝写入:
// 仅展示修改部分,其余逻辑保留 const fileFilter = async (req, file, cb) => { const fileFullName = 'public/' + file.originalname try { const existFile = await Attachment.findOne({ file_name: fileFullName }) if (existFile) { // 存在同名文件,拦截写入 cb(new Error(`文件${fileFullName}已存在`), false) return } // 无重复,允许写入 cb(null, true) } catch (err) { cb(err, false) } } const upload = multer({ storage: storage, fileFilter: fileFilter // 注入自定义校验逻辑 }); // 路由逻辑优化 router.post('/upload', upload.array('imgCollection'), async (req, res) => { try { // 批量写入数据库,用Promise.all确保所有写入完成再返回响应 const savePromises = req.files.map(file => { const attachment = new Attachment({ file_name: 'public/' + file.filename }) return attachment.save() }) await Promise.all(savePromises) res.status(201).json({ message: '文件保存成功' }) } catch (e) { // 错误处理:如果部分文件已经写入,删除残留文件 const fs = require('fs/promises') if (req.files?.length) { for (const file of req.files) { await fs.unlink(file.path).catch(() => {}) } } res.status(400).json({ message: e.message }) } })
方案2:新增预校验接口(适合大文件场景)
如果上传的是大文件,先传文件再校验会浪费带宽,你可以新增一个预校验接口:
- 前端选完文件后,先把所有文件名传给预校验接口,后端查MongoDB直接返回是否有重复
- 确认无重复后再调用上传接口传文件,优化用户体验
其他代码问题说明
你原路由中用reqFiles.map(async name => { await attachment.save() })的写法不会等待所有数据库写入操作完成就会返回响应,可能出现前端收到成功提示但数据库实际未写入的问题,修复后用Promise.all批量等待所有写入操作完成再返回。
内容的提问来源于stack exchange,提问作者Vladislav Sokolov
相关产品推荐
相关产品推荐

