Nuxt 3中用Multer上传文件到MongoDB遇Unexpected end of form错误
Nuxt3中Multer与readMultipartFormData冲突导致"Unexpected end of form"错误
问题现象
在Nuxt3服务器(非Express环境)中,尝试结合Multer上传文件并将数据存入MongoDB时遇到以下问题:
- 单独使用Multer可完成文件上传
- 加入
readMultipartFormData解析表单数据时,触发Unexpected end of form错误 - 移除Multer的上传监听器
callNodeListener后,又能正常通过readMultipartFormData解析数据
依赖版本
"nuxt": "^3.4.1", "mongoose": "^7.0.3", "multer": "^1.4.5-lts.1"
前端上传代码
let formData = new FormData(); formData.append("testcase_data", JSON.stringify(testCaseFormData.value)); for (var x = 0; x < selectedFiles.value.length; x++) { formData.append("images", selectedFiles.value[x]); } return await $fetch("/api/testcase/put", { method: "POST", body: formData, });
问题服务端代码
import TestCaseModel from "~~/server/models/TestCase.model"; import multer from "multer"; import { callNodeListener, readRawBody } from "h3"; import path from "path"; let filePath: string = "N/A"; const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "uploads"); }, filename: function (req, file, cb) { const uniqueSuffix = Date.now(); let ext = path.extname(file.originalname); let baseName = path.basename(file.originalname, ext); if (baseName.length > 5) { baseName = baseName.substring(0, 5); } filePath = baseName + "-" + uniqueSuffix + ext; cb(null, filePath); }, }); const upload: any = multer({ storage: storage, fileFilter: (req, file, cb) => { if ( file.mimetype == "image/png" || file.mimetype == "image/jpeg" || file.mimetype == "image/gif" ) { cb(null, true); } else { cb(new Error("File type not supported")); } }, }); export default defineEventHandler(async (event) => { const body: any = await readMultipartFormData(event); try { await TestCaseModel.findByIdAndUpdate(body._id, body); await callNodeListener(upload.array("images", 5), event.node.req, event.node.res); return { message: "Test case updated successfully" }; } catch (e) { throw createError({ statusCode: 422, statusMessage: "Unprocessable Entity", message: "Error while updating Test Case: " + e, }); } });
临时解决方案(使用Formidable)
import TestCaseModel from "~~/server/models/TestCase.model"; import FileModel from "~~/server/models/File.model"; import { readFiles } from "h3-formidable"; export default defineEventHandler(async (event) => { const { fields, files } = await readFiles(event, { includeFields: true, multiples: true, uploadDir: "./public/uploads", keepExtensions: true, }); const body = JSON.parse(fields.testcase[0]); const body_files: any = files.images; let imagesArray: any[] = []; if (body_files && body_files.length > 0) { body_files.forEach((img: any) => { imagesArray.push({ path: img.newFilename, mimetype: img.mimetype, test_case: body._id, }); }); } try { const uploadedFiles = await FileModel.insertMany(imagesArray); await TestCaseModel.findByIdAndUpdate(body._id, body).then((res) => { return TestCaseModel.findByIdAndUpdate( body._id, { $push: { files: { $each: uploadedFiles } } }, { upsert: true } ); }); return { message: "Test Case updated successfully" }; } catch (e) { throw createError({ message: "Error while updating Test Case: " + e, }); } });
问题原因与解决建议
原因
HTTP请求体数据流只能被读取一次。readMultipartFormData会先读取整个表单数据,导致后续Multer的callNodeListener再读取时,数据流已经耗尽,从而抛出Unexpected end of form错误。
解决建议
方案1:通过Multer直接获取表单字段
Multer处理完文件后,会将普通表单字段挂载到req.body上,无需单独调用readMultipartFormData:
export default defineEventHandler(async (event) => { // 先处理文件上传 await callNodeListener(upload.array("images", 5), event.node.req, event.node.res); // 从req.body获取表单字段 const testcaseData = JSON.parse(event.node.req.body.testcase_data); try { // 更新测试用例基本数据 await TestCaseModel.findByIdAndUpdate(testcaseData._id, testcaseData); // 处理多文件路径,这里需要调整filePath为数组存储 const filePaths = event.node.req.files.map((file: any) => file.path); await TestCaseModel.findByIdAndUpdate(testcaseData._id, { $push: { images: { $each: filePaths } } }); return { message: "Test case updated successfully" }; } catch (e) { throw createError({ statusCode: 422, statusMessage: "Unprocessable Entity", message: "Error while updating Test Case: " + e, }); } });
方案2:继续使用Formidable(推荐)
你的临时解决方案已能正常工作,Formidable与Nuxt3的h3框架兼容性更好,无需处理请求体读取冲突问题,后续维护成本更低。
内容的提问来源于stack exchange,提问作者Mel
相关产品推荐
相关产品推荐

