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

Cloudinary文件上传报错Cannot read property 'public_id' of undefined

Cloudinary上传文件时报错Cannot read property 'public_id' of undefined修复方案

问题现象

首次对接Cloudinary实现文件上传,排查完前置问题后上传功能失效,控制台在uploadResult逻辑处抛出如下错误:

Cannot read property 'public_id' of undefined

前端控制台可正常打印获取到的文件对象,但接口请求触发上述报错,无法完成文件上传和新闻数据入库流程。


涉及的问题代码

后端路由实现

router.post("/", verifyTokenAndAuthorization, async (req,res)=>{
        
        const {img,ressort, theme, title, content} = req.body;
    try{
        const uploadResult = await cloudinary.uploader.upload(img,{
            folder: MainNews
        }, (error, img)=>{
            if(error){
                console.log(error)
            }
            console.log("* Same image, uploaded with a custom public_id");
            console.log("*" + img.public_id);
            console.log("* " + img.url);
            waitForAllUploads("MainNews", error, img);
        });
        const newMainNews = new MainNews({
            ressort,
            theme,
            title,
            content,
            img: {
                public_id: uploadResult.public_id,
                url: uploadResult.secure_url
            }
        });
        const savedMainNews = await newMainNews.save();
        res.status(200).json(savedMainNews);
    } catch(error){
        res.status(403)
        throw new Error("Action failed");
    }
});

Redux Slice异步逻辑

export const createMainNews = createAsyncThunk("mainNews/create", async (mainnewsData, thunkAPI)=>{
    try{
        const token = thunkAPI.getState().auth.user.accessToken;
        return await mainnewsService.createMainNews(mainnewsData, token);
    } catch(error){
        const message = (error.response && 
            error.response.data && 
            error.response.data.message)
            || error.message
            || error.toString()
            return thunkAPI.rejectWithValue(message);
    }
})

Redux Service请求层

const API_URL = "http://localhost:5000/api/mainNews/";
const createMainNews = async (mainnewsData, token)=>{
    const config = {
        headers: {
            'Content-Type': `multipart/form-data`,
            token: `Bearer ${token}`,
          },
    }
    const response = await axios.post(API_URL, mainnewsData, config);
    return response.data;
}

前端表单实现

const [formdata, setFormdata] = useState(
    {
      ressort:"",
      theme:"",
      title:"",
      content:"",
    }
  )
 const {ressort,theme,title,content} = formdata;
  const [fileData, setFileData] = useState({
    img:""
  })
  const {img} = fileData;
  const fileInput = useRef(img);
  const fileChange = (e)=>{
    console.log(fileInput.current.files);
    setFileData(fileInput.current.files[0])
  }
  console.log(fileData); //works and it is a file
  const handleChange = (e)=>{
    setFormdata((prevState)=>({
      ...prevState,
      [e.target.name]: e.target.value
    }))
  }
  const onSubmit = (e)=>{
    e.preventDefault();
    const data = new FormData();
    data.append("img", fileData);
    data.append("ressort", formdata.ressort);
    data.append("theme", formdata.theme);
    data.append("title", formdata.title);
    data.append("content", formdata.content)
  
    const mainnewsData ={
      data,
    }
    dispatch(createMainNews(mainnewsData));
  }

问题根因

共有3处直接触发报错的代码问题:

  • 前端传参格式错误:组装完FormData实例后额外包了一层{data: data}对象传给axios,导致后端收到的请求体不是标准FormData格式,无法解析到img字段,最终传给Cloudinary的img参数实际为undefined。
  • 后端缺少文件解析中间件:multipart/form-data格式的文件上传请求,没有配置multer等对应中间件做解析,就算前端传参正确,也无法从请求中拿到上传的文件对象。
  • Cloudinary调用方式冲突:同时给cloudinary.uploader.upload传入回调函数、又用await接收返回值,混用写法会导致await拿到的uploadResult为undefined,后续访问public_id属性直接报错。

修复步骤

按顺序修改对应代码即可:

1. 修正前端传参逻辑

去掉FormData外层的多余包装,直接把FormData实例作为请求参数传递;删除手动设置的Content-Type请求头,浏览器会自动为multipart请求带上正确的boundary标识,避免后端解析失败:

// service层修改
const createMainNews = async (mainnewsData, token)=>{
    const config = {
        headers: {
            token: `Bearer ${token}`,
          },
    }
    const response = await axios.post(API_URL, mainnewsData, config);
    return response.data;
}

// 前端提交逻辑修改
const onSubmit = (e)=>{
    e.preventDefault();
    const data = new FormData();
    data.append("img", fileData);
    data.append("ressort", formdata.ressort);
    data.append("theme", formdata.theme);
    data.append("title", formdata.title);
    data.append("content", formdata.content)
    // 直接传入FormData实例,去掉多余包装
    dispatch(createMainNews(data));
}

2. 后端配置multer中间件解析文件

安装multer依赖,在路由文件中引入并配置内存存储(无需把文件存到本地服务器,直接转码后传给Cloudinary即可):

import multer from 'multer';
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });

// 路由加上single文件解析中间件,匹配前端append的img字段
router.post("/", verifyTokenAndAuthorization, upload.single('img'), async (req,res)=>{
  // 上传的文件存在req.file中,其他文本字段仍从req.body获取
  const {ressort, theme, title, content} = req.body;
  const imgFile = req.file;
  // 后续上传逻辑
})

3. 修正Cloudinary上传调用逻辑

去掉回调函数,统一用await接收返回结果;上传前把multer拿到的文件buffer转成dataURI格式传给Cloudinary,注意folder参数如果是固定字符串需要加引号:

// 工具函数:把文件buffer转成Cloudinary支持的dataURI格式
const fileToDataUri = (file) => `data:${file.mimetype};base64,${file.buffer.toString('base64')}`;

router.post("/", verifyTokenAndAuthorization, upload.single('img'), async (req,res)=>{
  const {ressort, theme, title, content} = req.body;
  try{
    // 仅用await接收返回值,不混用回调
    const uploadResult = await cloudinary.uploader.upload(fileToDataUri(req.file),{
        folder: "MainNews"
    });
    const newMainNews = new MainNews({
        ressort,
        theme,
        title,
        content,
        img: {
            public_id: uploadResult.public_id,
            url: uploadResult.secure_url
        }
    });
    const savedMainNews = await newMainNews.save();
    res.status(200).json(savedMainNews);
  } catch(error){
    res.status(500).json({message: "上传操作失败"})
  }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 15:09:45