Node.js使用Multer上传时如何校验待上传文件真实存在
问题原因
当前代码存在三个逻辑漏洞,才会出现未上传有效文件仍生成存储文件的异常:
multer.single('image')仅在请求携带image文件字段时才会触发fileFilter校验,如果请求完全没带该字段,multer默认不会抛出错误- 现有逻辑没有过滤0字节空文件,只要请求带了对应字段(哪怕是空文件流)就会执行磁盘写入,生成空的存储文件
fileFilter中直接传字符串作为错误参数不符合multer规范,会导致错误捕获异常
修复方案
按以下规则调整代码即可解决问题:
- 修正fileFilter的错误传参,所有错误必须传入Error实例
- 增加limits配置,设置最小文件大小过滤0字节空文件,同时可限制最大文件大小避免超大文件攻击
- 在upload回调中第一时间校验
req.file是否存在,不存在直接返回400错误,不执行后续写入、入库逻辑 - 建议同时校验文件MIME类型,避免仅修改文件后缀的非法jpg文件绕过校验
修正后完整代码
const path = require('path'); const fs = require('fs'); const multer = require('multer'); const storage = multer.diskStorage({ destination: (req, file, cb) => { const uploadDir = 'images'; // 自动创建上传目录,避免目录不存在导致写入报错 if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } cb(null, uploadDir) }, filename: (req, file, cb) => { // 替换原来硬编码的文件名,避免重名覆盖,保留原始后缀 const uniqueName = Date.now() + '-' + Math.round(Math.random() * 1E9) cb(null, `${uniqueName}${path.extname(file.originalname)}`) }, }) const upload = multer({ storage: storage, // 大小限制可按需调整:最小1字节拦截空文件,最大5M limits: { fileSize: 5 * 1024 * 1024, minFileSize: 1 }, fileFilter: (req, file, cb) => { const allowExt = '.jpg'; const allowMime = 'image/jpeg'; const currentExt = path.extname(file.originalname).toLowerCase(); if (currentExt !== allowExt || file.mimetype !== allowMime) { return cb(new Error(`Invalid file type, only ${allowExt} format is supported`)) } cb(null, true) } }).single('image') router.post('/', (req, res) => { upload(req, res, (err) => { // 先捕获multer抛出的类型错误、大小错误 if (err) { return res.status(400).send({ error: err.message }) } // 二次校验文件实体存在,拦截未传文件的请求 if (!req.file) { return res.status(400).send({ error: 'No valid image file found in request' }) } // 校验通过后再执行数据库入库逻辑 dbController.query(/* INSERT INTO images ... */) }) })
额外说明
- 原代码中filename硬编码为固定值会导致所有上传文件重名,后上传的文件会覆盖历史文件,上述代码已经替换为随机文件名实现
- 如果需要支持
.jpeg后缀的jpg文件,只需要在fileFilter的允许规则中增加对应的后缀和MIME类型image/jpeg即可
内容的提问来源于stack exchange,提问作者arrmani88
相关产品推荐
相关产品推荐

