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

Multer与React集成问题:图片无法保存至指定目录

问题排查:Multer未处理文件上传且无日志输出

正在学习Multer与React配合使用,发起POST请求时数据库成功接收数据,但Multer未将图片保存至backend/images目录,multer-config.js和控制器中的console.log均无输出,相关代码如下:


后端代码

multer-config.js

const multer = require("multer");

const MIME_TYPES = {
  "image/jpg": "jpg",
  "image/jpeg": "jpg",
  "image/png": "png",
};

const storage = multer.diskStorage({
  destination: (req, file, callback) => {
    console.log("oui");
    callback(null, "images");
  },

  filename: (req, file, callback) => {
    const name = file.originalname.split(" ").join("_");
    const extension = MIME_TYPES[file.mimetype];
    callback(null, name + Date.now() + "." + extension);
  },
});

const errorHandler = (err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send("Something went wrong!");
};

module.exports = multer({ storage: storage }).single("image");

控制器代码

exports.createCommInfos = (req, res, next) => {
  const infosObject = req.body;
  const infos = new CommInfos({
    ...infosObject,
    imageUrl: `${req.protocol}://${req.get("host")}/images/${
      req.body.picture.path
    }`,
  });

  infos
    .save()
    .then(() => {
      res.status(201).json({ message: " Infos enregistré !" + infos });
    })
    .catch((error) => {
      res.status(400).json({ error });
    });
};

路由配置

const multer = require("../middleware/multer-config.js");
router.post("/cominfos", multer, infosCtrl.createComInfos);
router.post("/recinfos", multer, infosCtrl.createRecInfos);
router.post("/orainfos", multer, infosCtrl.createOraInfos); 

前端POST函数

export async function postCommInfos(e, body, setCommPayment, setCommForm) {
    e.preventDefault(e);

      if (validForm(body)) {
        console.log(body)
            try {
      await fetch("http://localhost:8000/bioanimale/comminfos", {
        method: "POST",
        headers: {
          "Content-Type": "application/json;charset=utf-8",
        },
        body: JSON.stringify(body),
      });
      setCommPayment(true)
      setCommForm(false)
    } catch (e) {
      console.log(e);
    }
      }
  }

核心问题与修复方案

1. 前端请求格式错误,未发送FormData

Multer仅能处理multipart/form-data类型的请求,当前前端用JSON.stringify发送JSON格式数据,Multer会直接忽略该请求,因此不会执行任何文件处理逻辑,也不会输出日志。

修复方法:
前端需创建FormData对象承载表单数据(包含文件),且不要手动设置Content-Type,浏览器会自动生成正确的请求头:

export async function postCommInfos(e, body, setCommPayment, setCommForm) {
  e.preventDefault();

  if (validForm(body)) {
    try {
      const formData = new FormData();
      // 添加普通表单字段
      for (const key in body) {
        if (key !== 'picture') {
          formData.append(key, body[key]);
        }
      }
      // 添加文件字段,注意字段名需与Multer配置的`.single("image")`一致
      formData.append("image", body.picture);

      await fetch("http://localhost:8000/bioanimale/comminfos", {
        method: "POST",
        body: formData,
      });
      setCommPayment(true);
      setCommForm(false);
    } catch (e) {
      console.log(e);
    }
  }
}

2. 后端控制器获取文件路径的方式错误

文件上传后,Multer会将文件信息存入req.file对象,而非req.body。当前控制器通过req.body.picture.path获取路径的方式完全错误。

修复方法:
修改控制器代码,从req.file中提取文件名:

exports.createCommInfos = (req, res, next) => {
  const infosObject = {
    ...req.body,
    imageUrl: `${req.protocol}://${req.get("host")}/images/${req.file.filename}`
  };
  const infos = new CommInfos(infosObject);

  infos
    .save()
    .then(() => {
      res.status(201).json({ message: "Infos enregistré !", data: infos });
    })
    .catch((error) => {
      res.status(400).json({ error });
    });
};

3. 验证字段名一致性

Multer配置的是.single("image"),意味着它只会处理请求中名为image的文件字段,前端FormData中的文件字段名必须与此完全匹配,否则Multer无法识别文件。

4. 检查目标目录是否存在

Multer不会自动创建destination指定的目录,若backend下没有images文件夹,需手动创建:

mkdir images

内容的提问来源于stack exchange,提问作者Pierre Potin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:24:54