Express中Zod验证含多文件FormData时files属性未定义问题
问题:Express中使用Zod验证含多文件的FormData时,files属性提示undefined
HTML元素
<input type="file" id="files" name="files" multiple />
前端JavaScript代码
// Create a new FormData object const formData = new FormData(); // Append the form data to the FormData object formData.append("title", $("#title").val()); formData.append("content", $("#content").val()); // Append the files to the FormData object const files = document.getElementById("files").files; for (let i = 0; i < files.length; i++) { formData.append("files", files[i]); } // Use fetch to post the FormData object to the server // This will automatically set the Content-Type header to multipart/form-data const response = await fetch("http://localhost:3001/blog", { method: "POST", body: formData, }); console.log(await response.json());
Express路由代码
import express from "express"; import multer from "multer"; import { validate } from "../middleware/schemaValidator.js"; import { blogSchema } from "../schemas/blogSchema.js"; import { addBlogPost } from "../controllers/blogController.js"; const storage = multer.diskStorage({ destination: (_req, _file, cb) => { cb(null, "public/uploads/"); }, filename: (_req, file, cb) => { cb(null, file.originalname); }, }); const upload = multer({ storage }); const router = express.Router(); // Validate the blogSchema before adding a new blog post // The validate function will return an error if the request body does not match the schema // If there is no error, the addBlogPost function will be called router.post("/", upload.array("files", 2), validate(blogSchema), addBlogPost); export default router;
Zod Schema代码
import { z } from "zod"; import { zfd } from "zod-form-data"; const ACCEPTED_IMAGE_TYPES = ["image/png"]; export const blogSchema = zfd.formData({ title: z.string(), content: z.string(), files: z.array(z.instanceof(File)).refine((files) => { return files.every((file) => ACCEPTED_IMAGE_TYPES.includes(file.type)); }), });
错误信息
[ { "code": "invalid_type", "expected": "array", "received": "undefined", "path": [ "files" ], "message": "Required" } ]
问题原因
- multer的处理逻辑:multer解析上传的文件后,不会将文件放入
req.body,而是存放在req.files(使用upload.array时)中,而你的Zod验证逻辑默认只解析req.body,所以找不到files字段。 - Node端无File类:浏览器中的
File对象在Node.js环境中不存在,multer处理后的文件是包含originalname、mimetype、path等属性的自定义对象,用z.instanceof(File)验证必然失败。
解决方案
1. 修改验证中间件,合并req.body和req.files
在schemaValidator.js的validate函数中,把req.files合并到待验证的数据对象:
// schemaValidator.js import { z } from "zod"; export const validate = (schema) => async (req, res, next) => { try { // 合并body和multer解析后的files const data = { ...req.body, files: req.files }; await schema.parseAsync(data); next(); } catch (err) { res.status(400).json(err.errors); } };
2. 调整Zod Schema,适配multer返回的文件结构
替换z.instanceof(File)为描述multer文件对象的Schema:
import { z } from "zod"; const ACCEPTED_IMAGE_TYPES = ["image/png"]; // 定义multer返回的单个文件结构 const multerFileSchema = z.object({ originalname: z.string(), mimetype: z.string(), path: z.string(), size: z.number() }); export const blogSchema = z.object({ title: z.string(), content: z.string(), files: z.array(multerFileSchema).refine((files) => { return files.every((file) => ACCEPTED_IMAGE_TYPES.includes(file.mimetype)); }) });
3. 验证中间件顺序说明
upload.array必须在验证中间件之前执行,确保multer先解析文件到req.files,验证逻辑才能拿到正确的数据。
修改完成后,Zod即可正确识别files数组并验证文件类型。
内容的提问来源于stack exchange,提问作者RobertFrenette
相关产品推荐
相关产品推荐

