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

Multer上传图片报ENOENT路径错误 无法上传至Cloudinary问题咨询

问题排查:ENOENT路径报错+文件/Cloudinary上传失败

报错基础信息

  • 触发场景:上传带图片的文章时Redux DevTools抛出路径不存在错误
  • 程序错误访问路径:C:\xampp\frontside\public\uploads\
  • 实际正确存储路径:C:\xampp\htdocs\mern_redux_ntv\frontside\public\uploads\
  • 故障表现:目标上传文件夹无生成文件、Cloudinary端未收到任何上传资源

根因定位

一共4个叠加问题导致故障:

  • Multer相对路径解析逻辑错误:配置中写的../../frontside/public/uploads是相对路径,解析基准是Node服务启动时的命令行工作目录,而非multer配置文件自身的存放位置。当你在C:\xampp目录层级启动服务时,路径解析会直接跳转到C:\xampp\frontside\public/uploads\,和预期路径差了两层目录,直接触发ENOENT找不到目录的报错。
  • Multer配置字段拼写错误:diskStorage配置中控制文件名的字段是全小写的filename,你写成了驼峰格式fileName,Multer识别不到该配置,无法正确生成写入文件名,直接导致文件落盘失败。
  • 存储路径格式非法:此前测试时配置的http://localhost:3000/frontside/public/uploads是HTTP网络地址,不属于本地文件系统路径,Multer负责本地磁盘写入,无法识别网络地址作为存储目标。
  • 前后端请求与传参逻辑错误:
    1. 前端上传文件时注释掉了multipart/form-data请求头,改用application/json格式,文件对象无法被正确序列化传输,后端根本接收不到文件流
    2. 后端接口错误从req.body.img取文件传给Cloudinary,经过Multer中间件处理后,上传的文件信息会挂载在req.file对象上,req.body里只存普通表单字段,拿不到文件资源自然无法上传到Cloudinary。

可行修复方案

第一步:修正Multer存储配置

用Node内置path模块拼接绝对路径,彻底解决相对路径解析偏差问题,同时修正字段拼写错误:

const multer = require("multer");
const path = require("path");

const storage = multer.diskStorage({
  destination: (req, file, callback) => {
    // 基于当前配置文件路径拼接绝对路径,不受服务启动目录影响,可自行调整../层级匹配实际目录结构
    const uploadPath = path.resolve(__dirname, '../../frontside/public/uploads');
    callback(null, uploadPath);
  },
  // 修正字段名为Multer要求的全小写filename
  filename: (req, file, callback) => {
    callback(null, `${Date.now()}--${file.originalname}`);
  }
})

const upload = multer({ storage: storage });
module.exports = upload;

注意:提前手动创建好frontside/public/uploads目录,Multer不会自动递归生成不存在的父目录,目录不存在也会触发ENOENT报错。配置完成后可打印uploadPath变量,确认输出路径和预期的C:\xampp\htdocs\mern_redux_ntv\frontside\public\uploads\完全一致。

第二步:修正前端上传请求配置

传文件必须用FormData格式,不要手动设置Content-Type,让浏览器自动携带正确的boundary分隔参数:

const createMainNews = async (mainnewsData, token) => {
  // 将普通表单对象转为FormData格式
  const formData = new FormData();
  Object.keys(mainnewsData).forEach(key => {
    formData.append(key, mainnewsData[key]);
  });

  const config = {
    headers: {
      // 禁止手动设置Content-Type,否则会丢失boundary参数导致后端解析失败
      token: `Bearer ${token}`,
    },
  }
  const response = await axios.post(API_URL, formData, config);
  console.log(response);
  return response.data;
}

第三步:修正后端接口逻辑

从req.file获取Multer存储的本地文件路径,再传给Cloudinary上传,补全错误返回逻辑:

router.post("/", upload.single("img"), verifyTokenAndAuthorization, async (req, res) => {
  const { ressort, theme, title, content } = req.body;
  try {
    // 先校验文件是否上传成功
    if (!req.file) {
      return res.status(400).json({ message: "未接收到上传的图片文件" });
    }
    // 从req.file.path取本地存储的文件绝对路径传给Cloudinary
    const uploadResult = await cloudinary.uploader.upload(req.file.path, {
      upload_preset: "Mern_redux-practice",
      resource_type: "auto",
    });
    console.log("Upload successful", JSON.stringify(uploadResult, null, 2));
    // 此处可追加新闻数据入库等自定义业务逻辑
    return res.status(200).json(uploadResult);
  } catch (error) {
    console.log("upload error", JSON.stringify(error, null, 2));
    return res.status(500).json({ message: "上传失败", error: error.message });
  }
});

优化建议

如果不需要在本地留存上传文件副本,可以直接将Multer存储模式改为memoryStorage,文件会以buffer形式存在内存中,无需落盘即可直接传给Cloudinary,能彻底规避本地路径配置问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:06:21