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

如何在Multer上传文件前用Joi校验multipart/form-data请求?

问题:Multer上传文件前校验字段,阻止无效请求的文件上传

我正尝试通过Postman向Node.js后端服务上传文件,同时附带email、username等额外字段。已通过Multer实现文件上传至云端的功能,但希望在额外字段缺失时,阻止Multer将文件上传至云端。尝试用Joi校验请求Schema,但遇到了问题。

第一次尝试的代码(校验不生效)

const upload = multer({
  storage: new MulterGoogleCloudStorage({
    autoRetry: true,
  })
});

function validateRequest(req: any, res: any, next: () => void) {
    const validator = metadataValidator.validate(req.body);
    if (validator.error) {
      return res.status(400).send("Missing properties.");
    }

    next();
}

router.post('/upload', validateRequest, upload.array("files"), (req, res) => {
 ...
})

此时因为Multer尚未解析form-data,req.body为空,导致Joi校验无法生效。

第二次尝试的代码(校验生效但文件已上传)

调整中间件顺序后,Multer先解析并上传文件,再执行校验:

router.post('/upload', upload.array("files"), validateRequest, (req, res) => {
 ...
})

此时校验可以正常工作,但即使校验失败,文件已经被上传至云端,不符合需求。

当前问题

  • 期望结果:发送缺少必填字段的请求时,请求直接失败,文件不会被上传。
  • 实际结果:无论校验是否失败,Multer都会将文件上传至云端。

此前尝试过相关解决方案但未解决问题。


解决方案

方法1:内存暂存文件,校验通过后再上传至云端

先将文件暂存到内存,执行字段校验,校验通过后再上传到谷歌云存储,失败则直接丢弃内存中的文件:

// 初始化内存存储的Multer,仅将文件暂存到内存
const memoryUpload = multer({ storage: multer.memoryStorage() });

// 字段校验中间件
function validateRequest(req: any, res: any, next: () => void) {
    const validator = metadataValidator.validate(req.body);
    if (validator.error) {
      // 校验失败,直接返回,内存文件会被自动回收
      return res.status(400).send("Missing properties.");
    }
    next();
}

// 自定义上传到谷歌云的中间件
async function uploadToGCS(req: any, res: any, next: () => void) {
  try {
    const gcsStorage = new MulterGoogleCloudStorage({ autoRetry: true });
    // 遍历内存中的文件,逐个上传到云端
    for (const file of req.files) {
      await new Promise((resolve, reject) => {
        gcsStorage._handleFile(req, file, (err) => {
          err ? reject(err) : resolve(null);
        });
      });
    }
    next();
  } catch (err) {
    res.status(500).send("File upload failed.");
  }
}

// 路由顺序:内存解析文件 -> 字段校验 -> 云端上传 -> 业务逻辑
router.post('/upload', memoryUpload.array("files"), validateRequest, uploadToGCS, (req, res) => {
  // 后续业务处理
  res.status(200).send("Files uploaded successfully.");
})

方法2:利用Multer的fileFilter实现异步校验

通过fileFilter在文件上传前解析并校验字段,校验不通过则拒绝文件上传:

const busboy = require('busboy');

const upload = multer({
  storage: new MulterGoogleCloudStorage({ autoRetry: true }),
  fileFilter: (req, file, cb) => {
    // 使用busboy先解析form-data中的字段
    const bb = busboy({ headers: req.headers });
    let requestBody: any = {};

    bb.on('field', (fieldName, value) => {
      requestBody[fieldName] = value;
    });

    bb.on('finish', () => {
      req.body = requestBody;
      // 执行Joi校验
      const validator = metadataValidator.validate(requestBody);
      if (validator.error) {
        // 校验失败,拒绝文件上传
        cb(new Error("Missing properties."), false);
      } else {
        // 校验通过,允许上传
        cb(null, true);
      }
    });

    req.pipe(bb);
  }
});

// 路由直接使用该上传中间件
router.post('/upload', upload.array("files"), (req, res) => {
  // 业务逻辑处理
})

注意:该方法需注意busboy与Multer的兼容性,因为Multer本身基于busboy实现,手动解析可能存在流式处理的冲突,方法1的稳定性更高。


内容的提问来源于stack exchange,提问作者Itonroe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 21:45:15