Next.js中Cloudinary API单图上传提交时出现400错误
问题分析与修复方案
核心错误点
1. 上传的是Data URL而非原始文件对象
图片选择组件中,你用FileReader把文件转成Data URL存在image状态,但上传时将这个字符串传给了FormData。Cloudinary需要接收原始文件对象,而非base64编码字符串,这是导致400请求错误的核心原因。
2. Cloudinary路由文件获取方式错误
使用express-fileupload中间件时,文件会被存在req.files对象里,而非req.file,后端因此无法获取到上传的文件。
3. 创建文章逻辑缺失且存在语法错误
createProductHandler中上传图片后未调用创建文章的API,还存在未定义的data变量以及重复的router.push。
具体修复步骤
步骤1:修改图片选择组件,保存原始文件对象
无需将文件转成Data URL,直接保存原始文件,同时生成预览用的Data URL:
// 图片选择组件页面 - 组件顶部新增状态 const [previewImage, setPreviewImage] = useState(""); // 修改handleImages函数 const handleImages = (e) => { const file = e.target.files[0]; // 保存原始文件对象 setImage(file); // 生成预览图的Data URL const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (e) => { setPreviewImage(e.target.result); }; }; // 修改handleRemove函数 const handleRemove = () => { setImage(null); setPreviewImage(""); }; // 修改预览图渲染部分 {!previewImage ? ( <img src="../../../images/no_image.png" alt="" /> ) : ( <div className={styles.images__main_grid_wrap}> <div className={styles.blur}></div> <img src={previewImage} alt="" /> <div className={styles.images__main_grid_actions}> <button onClick={handleRemove}> <RiDeleteBin7Fill /> </button> <button> <RiShape2Line /> </button> </div> </div> )}
步骤2:修复创建文章页面的上传逻辑
上传时传递原始文件对象给FormData,并补充创建文章的API调用:
// 创建文章页面 - 修改createProductHandler函数 const createProductHandler = async () => { try { setLoading(true); let uploaded_image = null; if (image) { const path = "product images"; let formData = new FormData(); formData.append("path", path); // 传递原始文件对象 formData.append("file", image); uploaded_image = await uploadImages(formData); } // 调用创建文章的API const createRes = await axios.post("/api/product", { ...product, image: uploaded_image?.url, public_id: uploaded_image?.public_url, }); setLoading(false); toast.success(createRes.data.message); router.push("/admin/dashboard/product/all"); } catch (error) { setLoading(false); toast.error(error.response?.data?.message || "创建失败"); } };
步骤3:修复Cloudinary路由的文件获取逻辑
从req.files中获取文件(对应FormData里的key"file"):
// Cloudinary API路由处理页面 - 修改router.post逻辑 router.post(async (req, res) => { try { const { path } = req.body || {}; // 从req.files获取上传的文件 const file = req.files?.file; if (!file) { return res.status(400).json({ message: "未选择文件" }); } const img = await uploadToCloudinaryHandler(file, path); removeTmp(file.tempFilePath); res.json(img); } catch (error) { console.log(error); return res.status(500).json({ message: error.message }); } });
步骤4:完善文章创建API,添加图片字段
确保News模型包含image和public_id字段,然后修改路由:
// 文章创建API路由处理页面 - 修改router.post逻辑 router.post(async (req, res) => { try { db.connectDb(); const slug = slugify(req.body.name); const newProduct = new Product({ ...req.body, slug, image: req.body.image, public_id: req.body.public_id, }); await newProduct.save(); res.status(200).json({ message: "文章创建成功" }); db.disconnectDb(); } catch (error) { res.status(500).json({ message: error.message }); } });
内容的提问来源于stack exchange,提问作者Chidimma Nworah
相关产品推荐
相关产品推荐

