解决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
相关产品推荐
相关产品推荐

