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

MERN项目使用multer上传PDF时出现Cannot read property 'path' of undefined错误

MERN栈PDF上传报错:Cannot read property 'path' of undefined 排查方案

问题背景

在MERN栈项目中,前端上传PDF文件时触发Cannot read property 'path' of undefined错误,后端通过Postman测试可正常上传文件并将路径存入数据库,问题出在前端代码逻辑中。


相关代码片段

后端路由

const express = require("express");
const { upload } = require("../middlewares/uploadMiddleware");
const { registerResearchSheet } = require("../controllers/researchSheetControllers");

const router = express.Router();
router.route("/").post(upload.single("file"), registerResearchSheet);

后端上传中间件

const multer = require("multer");

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "backend/uploads/");
  },
  filename: function (req, file, cb) {
    cb(
      null,
      new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname
    );
  },
});

const fileFilter = (req, file, cb) => {
  if (file.mimetype === "application/pdf") {
    cb(null, true);
  } else {
    cb(null, false);
  }
};

const upload = multer({ storage: storage, fileFilter: fileFilter });

module.exports = { upload };

后端控制器

const asyncHandler = require("express-async-handler");
const ResearchSheet = require("../models/researchSheetModel");

const registerResearchSheet = asyncHandler(async (req, res) => {
  const {
    title,
    director,
    coordinator,
    students,
    reviewers,
    proposalType,
    investigationType,
    status,
  } = req.body;

  if (!req.file) {
    res.status(400);
    throw new Error("The file hasn't be uploaded correctly"); // 触发错误的位置
  }
  const file = req.file.path;

  const researchSheetExists = await ResearchSheet.findOne({ title, students });
  if (researchSheetExists) {
    res.status(400);
    throw new Error("Title or students can't be repeated");
  }

  const researchSheet = await ResearchSheet.create({
    title,
    file,
    director,
    coordinator,
    students,
    reviewers,
    proposalType,
    investigationType,
    status,
  });

  if (researchSheet) {
    res.status(201).json({
      _id: researchSheet._id,
      title: researchSheet.title,
      file: researchSheet.file,
      director: researchSheet.director,
      coordinator: researchSheet.coordinator,
      students: researchSheet.students,
      reviewers: researchSheet.reviewers,
      proposalType: researchSheet.proposalType,
      investigationType: researchSheet.investigationType,
      status: researchSheet.status,
    });
  } else {
    res.status(400);
    throw new Error("An error has ocurred");
  }
});

前端提交逻辑

const [file, setFile] = useState(null);

const singleFileUpload = (e) => {
  setFile(e.target.files[0]);
};

const submitHandler = async (e) => {
  e.preventDefault();
  const formData = new FormData();
  formData.append("title", title);
  formData.append("file", file);
  formData.append("director", director);
  formData.append("coordinator", coordinator);
  formData.append("students", students);
  formData.append("proposalType", proposalType);
  formData.append("investigationType", investigationType);

  console.log(file); // 此处能正确打印文件信息

  if (
    !title ||
    !coordinator ||
    !students ||
    !proposalType ||
    !investigationType
  ) {
    setMessage("You must fill all the fields");
  } else {
    dispatch(registerResearchSheetAction(formData));
    // history.push("/listresearchsheets");
  }
};

// 表单部分
<Form.Control
  className="mb-3"
  type="file"
  accept=".pdf"
  onChange={(e) => singleFileUpload(e)}
/>
<Button type="submit">Register</Button>

Redux Action代码

export const registerResearchSheetAction =
  (
    title,
    file,
    director,
    coordinator,
    students,
    reviewers,
    proposalType,
    investigationType,
    status
  ) =>
  async (dispatch) => {
    try {
      dispatch({
        type: RESEARCHSHEET_REGISTER_REQUEST,
      });

      const formData = new FormData();
      formData.append("file", file);
      formData.append("title", title);
      formData.append("director", director);
      formData.append("coordinator", coordinator);
      formData.append("students", students);
      formData.append("reviewers", reviewers);
      formData.append("proposalType", proposalType);
      formData.append("investigationType", investigationType);
      formData.append("status", status);

      const config = {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      };

      const { data } = await axios.post(
        "/api/researchsheets",
        formData,
        config
      );

      dispatch({ type: RESEARCHSHEET_REGISTER_SUCCESS, payload: data });
    } catch (error) {
      dispatch({
        type: RESEARCHSHEET_REGISTER_FAIL,
        payload:
          error.response && error.response.data.message
            ? error.response.data.message
            : error.message,
      });
    }
  };

错误原因

前端submitHandler已经正确组装了包含文件的FormData,并将其传给registerResearchSheetAction,但Action的参数定义却是多个单独字段(title、file等),导致:

  1. Action接收的第一个参数是前端传来的FormData,而非预期的title,后续参数全为undefined
  2. Action里重新组装的FormData中,file字段值为undefined,后端multer无法解析到文件,因此req.file为undefined,触发报错

另外,手动设置Content-Type: multipart/form-data会丢失axios自动生成的boundary分隔符,也会导致后端无法正确解析FormData。


修复方案

步骤1:修改Redux Action的参数接收逻辑

直接接收前端传来的FormData,不再拆解为单独参数:

export const registerResearchSheetAction = (formData) => async (dispatch) => {
  try {
    dispatch({
      type: RESEARCHSHEET_REGISTER_REQUEST,
    });

    // 无需重新组装FormData,直接使用传入的实例
    const config = {
      headers: {
        // 移除手动设置的Content-Type,axios会自动处理
        "Content-Type": "multipart/form-data",
      },
    };

    const { data } = await axios.post(
      "/api/researchsheets",
      formData,
      config
    );

    dispatch({ type: RESEARCHSHEET_REGISTER_SUCCESS, payload: data });
  } catch (error) {
    dispatch({
      type: RESEARCHSHEET_REGISTER_FAIL,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    });
  }
};

步骤2:前端补充字段校验

在submitHandler中增加文件非空校验,避免空文件提交:

if (
  !title ||
  !coordinator ||
  !students ||
  !proposalType ||
  !investigationType ||
  !file // 新增文件校验
) {
  setMessage("You must fill all the fields and select a PDF file");
} else {
  dispatch(registerResearchSheetAction(formData));
}

步骤3(可选):优化Action的请求头设置

完全移除手动设置的Content-Type,让axios自动处理,确保boundary正确:

const config = {
  headers: {},
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 18:11:10