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

React前端无法上传文件至Cloudinary,请求排查问题

问题描述

我正尝试通过React前端将图片上传至Cloudinary平台,已编写名为upload image的后端API。用Postman测试时文件能正常上传到Cloudinary,但通过React前端上传时,后端提示未收到文件。请帮忙审阅代码,指出错误和后端无法接收文件的原因。


后端代码

dataUri.js

import DataUriParser from "datauri/parser.js";
import path from "path";

const getDataURI = (file) => {
    const parser = new DataUriParser();
    const extName = path.extname(file.originalname).toString();
    return parser.format(extName, file.buffer);
};

export default getDataURI;

multerMiddleware.js(路由中使用)

import multer from "multer";

const storage = multer.memoryStorage();

const singleUpload = multer({ storage }).single("file");

export default singleUpload;

图片控制器代码

export const uploadImagesUrl = expressAsyncHandler(async (req, res) => {
    try {

        const field = req.params.field;
        try {
            const file = req.file;

            if (!file) {
                return res.status(400).json({ error: "No file received" });
            }

            const fileURI = getDataURI(file);

            const imgUpload = await cloudinaryConfig();

            const { cloud_name } = imgUpload.config();

            const result = await imgUpload.uploader.upload(fileURI.content, {
                folder: "JewelryMine",
            });

            const image = await Image.findByIdAndUpdate(process.env.IMAGE_CONFIGURATION_ID);

            const image_url = `https://res.cloudinary.com/${cloud_name}/image/upload/${result.public_id}`;

            const updateObj = {
                $set: {
                    [`${field}.public_id`]: result.public_id,
                    [`${field}.image_url`]: image_url,
                },
            };

            if (image) {
                await image.updateOne(updateObj, options)
                return res.json({ message: "File Uploaded" });
            } else {
                return res.status(404).json({ message: "Field not found" });
            }
        } catch (error) {
            console.error(error);
            res.status(500).json({ error: "Upload failed" });
        }

    } catch (error) {
        res.status(500);
        throw new Error("Server Error. Please try after sometime.");
    }
});

React前端代码

表单部分

<div className='mt-2'>
    <form onSubmit={submitHandler}>
        <div className='flex flex-col'>
            <input type='file' accept='image/*' onChange={handleImage} required />
        </div>
        <div className='flex flex-col'>
          <label >Preview</label>
          <img src={imgPreview} required />
        </div>
        <div className='mt-4'>
            <button type='submit'>Update</button>
        </div>
    </form>
</div>

处理函数部分

const [image, setImage] = useState(null);
const [imgPreview, setImgPreview] = useState("");
const [updateImageUrl] = useUpdateImageUrlMutation();

const handleImage = async (event) => {
    setImage(event.target.files[0]);
    const url = await URL.createObjectURL(event.target.files[0] || image);
    setImgPreview(url);
};

const submitHandler = async (event) => {
    event.preventDefault();

    const formData = new FormData();
    formData.append("file", image);

    try {
        const res = await updateImageUrl({ field, formData }).unwrap();

        if (res) {
            toast.success(res.message || res.data.message);
        }
    } catch (err) {
        toast.error(err?.data?.message || err?.error || err?.data?.error);
    }
};

问题原因及解决方案

核心问题在于React端发送请求时,没有正确以FormData格式传递文件,而是将FormData作为普通JSON对象的一部分发送,导致后端multer无法解析到文件。

具体来说,调用updateImageUrl时传入的{ field, formData }会让RTK Query将整个对象序列化为JSON,而FormData无法被正确序列化为JSON,后端自然接收不到文件。

修复步骤:

  1. 调整请求发送方式:确保请求的body直接是FormData,浏览器会自动添加正确的multipart/form-data请求头及边界标识,无需手动设置。
  2. 修改RTK Query的mutation配置:在mutation的query函数中返回FormData作为body,同时指定请求方法。

示例代码:

// 假设你的mutation定义
const updateImageUrlMutation = createApi({
  // ...其他配置
  endpoints: (builder) => ({
    updateImageUrl: builder.mutation({
      query: ({ field, formData }) => ({
        url: `/your-api-path/${field}`, // 替换为实际API路径
        method: 'POST',
        body: formData, // 直接传递FormData
      }),
    }),
  }),
});

// 修正后的submitHandler
const submitHandler = async (event) => {
    event.preventDefault();
    if (!image) return;

    const formData = new FormData();
    formData.append("file", image);

    try {
        const res = await updateImageUrl({ field, formData }).unwrap();
        toast.success(res.message || res.data.message);
    } catch (err) {
        toast.error(err?.data?.message || err?.error || err?.data?.error);
    }
};

额外优化点:

  • URL.createObjectURL是同步方法,不需要await,修正handleImage:
    const handleImage = (event) => {
        const selectedFile = event.target.files[0];
        setImage(selectedFile);
        const url = URL.createObjectURL(selectedFile);
        setImgPreview(url);
    };
    
  • 确认后端路由正确挂载singleUpload中间件:
    router.post('/upload/:field', singleUpload, uploadImagesUrl);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:17:33