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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 09:57:15