You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js使用Multer上传时如何校验待上传文件真实存在

问题原因

当前代码存在三个逻辑漏洞,才会出现未上传有效文件仍生成存储文件的异常:

  1. multer.single('image') 仅在请求携带image文件字段时才会触发fileFilter校验,如果请求完全没带该字段,multer默认不会抛出错误
  2. 现有逻辑没有过滤0字节空文件,只要请求带了对应字段(哪怕是空文件流)就会执行磁盘写入,生成空的存储文件
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:43:12