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

解决Express控制器中Multipart表单提交数据/附件为空的问题

解决FormData上传附件到MongoDB的问题

问题根源梳理

  • 前端:直接将FileList整体append到FormData无效,且手动设置multipart/form-data请求头会丢失自动生成的boundary,导致后端无法解析表单
  • 后端:同时混用multer和formidable导致解析冲突,fileFilter判断逻辑错误,路由未正确挂载multer解析中间件

前端代码修改

1. 正确处理FileList上传

遍历FileList中的每个文件逐个append,确保后端能接收到多文件数组:

const handlePaymentSubmit = async (e) => {
    e.preventDefault();
    let payment = new FormData();
    payment.append("payee", user.id);
    payment.append("amount", paymentAmount);
    payment.append("code", paymentCode);
    payment.append("method", paymentMethod);
    payment.append("applicationId", applicationId);
    // 遍历FileList,逐个添加文件
    if (attachments.length > 0) {
      Array.from(attachments).forEach(file => {
        payment.append("attachments", file);
      });
    }
    await createPayment(payment);
    isSuccess && dispatch(reset());
  };

2. 移除RTK手动设置的Content-Type

FormData会自动生成带boundary的正确请求头,手动设置会破坏解析逻辑:

createPayment: builder.mutation({
      query: (payment) => ({
        url: "/api/payments/",
        method: "POST",
        body: payment,
        // 删掉手动设置的Content-Type
        // headers: {
        //   "Content-Type": "multipart/form-data;",
        // },
      }),

后端代码修改

1. 修复Multer的fileFilter判断逻辑

原代码用赋值运算符导致所有文件都被放行,改为正确的类型校验:

import multer from "multer";

var storeImage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "uploads/payments");
  },
  filename: function (req, file, cb) {
    let ext = file.originalname.substring(file.originalname.lastIndexOf("."));
    let name = file.originalname.substring(0, file.originalname.indexOf("."));
    cb(null, name + "-" + Date.now() + ext);
  },
});

export const storage = multer({
  storage: storeImage,
  fileFilter: function (req, file, cb) {
    const allowedTypes = ["image/png", "image/jpg", "image/jpeg", "application/pdf"];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      console.log("Only pdf, jpg and Png Supported");
      cb(new Error("Only pdf, jpg and Png Supported"), false);
    }
  },
  limits: {
    fileSize: 1024 * 1024 * 2, // 2MB限制
  },
});

2. 路由挂载Multer中间件

在支付创建路由中使用storage.array('attachments')接收多文件:

// 示例路由文件(如routes/payments.js)
import express from 'express';
import { createPayment } from '../controllers/paymentController.js';
import { storage } from '../middleware/multerConfig.js';

const router = express.Router();

// 用multer处理attachments字段的多文件上传
router.post('/', storage.array('attachments'), createPayment);

export default router;

3. 重构创建控制器代码

移除formidable依赖,直接使用multer解析后的req.body和req.files:

import asyncHandler from "express-async-handler";
import Payment from "../../models/payment.js";

export const createPayment = asyncHandler(async (req, res) => {
  const { payee, amount, code, method, applicationId } = req.body;

  if (!payee || !amount || !code || !method || !applicationId) {
    res.status(400);
    throw new Error("请填写所有必填字段");
  }

  // 处理附件,兼容无文件的情况
  let attachments = [];
  if (req.files && req.files.length > 0) {
    attachments = req.files.map((attachment) => ({
      path: attachment.path,
      name: attachment.filename,
      size: `${(attachment.size / 1000).toFixed(2)} KBs`,
    }));
  }

  const payment = await Payment.create({
    payee,
    amount,
    code,
    method,
    applicationId,
    attachments,
  });

  res.status(201).json(payment); // 创建成功用201状态码更规范
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 05:27:28