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

如何使用Multer安全处理对象数组形式的多文件上传?

问题:Multer安全处理数组对象中的文件上传

场景与需求

我正在构建一个包含多个试题的考试Demo,每个试题的结构如下:

[{
  image: an_image_file,
  audio: audio_file,
  text: just_text,
  another_text: text
}]

当前问题

我无法用Multer处理这种嵌套在对象数组里的图片和音频文件。目前用upload.any()能实现功能,但这个方法存在安全隐患,希望找到更安全的处理方案。

前端代码

const onSubmit = async (data) => {
    const form = new FormData();
    data.questions.forEach((question, index) => {
      form.append(`questions[${index}].img`, question.img);
      form.append(`questions[${index}].audio`, question.audio);
      form.append(`questions[${index}].text`, question.text);
    });
    try {
      const result = await axios.post(
        'http://localhost:1445/api/v1/exam',
        form
      );
      console.log(result);
    } catch (err) {}
  };

后端代码

const multer = require('multer');
const compression = require('compression');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const date = new Date().getTime();

const app = express();
app.use(express.json());
app.use('*', cors());
app.use(compression());

const upload = multer({
  limits: 100000000,

  storage: multer.diskStorage({
    destination: (req, file, cb) => {
      console.log(__dirname);
      const dir = path.join(__dirname, '..', 'public');
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
      }
      cb(null, dir);
    },
    filename: (req, file, cb) => {
      console.log('body', req.body);
      cb(null, `${date}_${file.filename}`);
    },
  }),
});

const router = express.Router();
const api = router.post(
  '/exam/',
  upload.any(),
  (req, res, next) => {
    console.log(req.body);
    console.log(req.files);
    return res.json({ ok: 'ok' });
  }
);

安全处理方案

1. 限制允许的文件字段

用fileFilter配合字段名正则验证,替代upload.any(),只接受符合格式的字段:

const upload = multer({
  limits: 10485760, // 缩小为10MB,按需调整
  storage: multer.diskStorage({/* 保留原存储配置 */}),
  fileFilter: (req, file, cb) => {
    // 验证字段名是否符合questions[索引].img/audio格式
    const validField = /^questions\[\d+\]\.(img|audio)$/.test(file.fieldname);
    if (!validField) {
      return cb(new Error('非法文件字段'));
    }
    cb(null, true);
  }
});

2. 严格限制文件类型

在fileFilter中添加MIME类型验证,只允许指定的图片、音频格式:

fileFilter: (req, file, cb) => {
  // 先验证字段名
  const validField = /^questions\[\d+\]\.(img|audio)$/.test(file.fieldname);
  if (!validField) {
    return cb(new Error('非法文件字段'));
  }

  // 根据字段后缀判断允许的文件类型
  const allowedTypes = {
    img: ['image/jpeg', 'image/png', 'image/gif'],
    audio: ['audio/mpeg', 'audio/wav', 'audio/ogg']
  };
  const fieldType = file.fieldname.split('.')[1];
  if (allowedTypes[fieldType].includes(file.mimetype)) {
    cb(null, true);
  } else {
    cb(new Error(`不允许的${fieldType}文件类型`));
  }
}

3. 完善文件命名逻辑

避免使用原始文件名,改用唯一标识符防止冲突和路径遍历:

const { v4: uuidv4 } = require('uuid'); // 需要安装uuid包

filename: (req, file, cb) => {
  const ext = path.extname(file.originalname);
  const uniqueName = `${date}_${uuidv4()}${ext}`;
  cb(null, uniqueName);
}

4. 额外安全措施

  • 验证请求中试题数量的合理性,防止恶意提交大量文件
  • 给上传目录设置严格的文件权限(如只读)
  • 考虑将文件存储到云存储服务,减少服务器本地存储的安全风险

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 05:17:24