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

Next.js前端上传图片至Cloudinary时遭遇CORS错误求助

MERN栈头像上传Cloudinary遭遇CORS错误

场景描述

首次使用MERN栈开发用户头像上传功能,前端代码如下:

// Settings.tsx
import React, { useEffect, useState } from "react";
import axios from "axios";
import Image from "next/image";
import iconDefault from "../assets/random/user.png";

const Settings = () => {
  const [isEditing, setIsEditing] = useState<boolean>(false);
  const [file, setFile] = useState<any | null>(null);
  const [picture, setPicture] = useState<any | null>(null);

  const cookies = new Cookies();
  const user = getUserCookie();
  const [updatedUser, setUpdatedUser] = useState<User>(user);

  const handleEdit = () => {
    setIsEditing(!isEditing);
  };

  const handleInputChange = (
    e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
  ) => {
    const { name, value } = e.target;
    setUpdatedUser((prevUser) => ({
      ...prevUser,
      [name]: value,
    }));
  };

  const upload_preset = ".....";
  const cloud_name = ".....";

  const submitImage = async () => {
    const data = new FormData();
    data.append("file", file);
    data.append("upload_preset", upload_preset);
    data.append("cloud_name", cloud_name);

    try {
      const res = await axios.post(
        `https://api.cloudinary.com/v1_1/${cloud_name}/image/upload`,
        data
      );
      const cloudinaryUrl = res.data.secure_url;
      setPicture(cloudinaryUrl);

      const updatedUserWithImage = { ...updatedUser, icon: cloudinaryUrl };
      await editUser(updatedUserWithImage);
    } catch (err) {
      console.log("error uploading icon", err);
    }
  };

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    try {
      if (file) {
        await submitImage();
      } else {
        await editUser(updatedUser);
      }
      window.location.reload();
    } catch (err) {
      console.log("error updating icon: ", err);
    }
  };

  const editUser = async (updatedUser: User) => {
    const token = cookies.get("token");

    try {
      await axios.patch(
        process.env.MONGODB_URL + `/users/${user?._id}`,
        updatedUser,
        {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }
      );

      const response = await axios.get(
        process.env.MONGODB_URL + `/users/${user?._id}`,
        {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }
      );

      const updatedUserFromServer = response.data;
      document.cookie = `user=${JSON.stringify(updatedUserFromServer)}; path=/`;
      console.log("Updated User:", updatedUserFromServer);
    } catch (err) {
      console.log("error updating user: ", err);
    }
  };

  return (
    <div className="settings">
      <main>
        <header className="header__welcome card ">
          <div>
            {user ? <h1>Hello, {user.name}!</h1> : <h1>Loading user...</h1>}
            <h2>Edit here your profile info</h2>
          </div>
          
        </header>
        <div className="form-container">
          <form onSubmit={handleSubmit} className="form-settings">
            <div className="form-settings__left">
              <div>
                {user.icon ? (
                  <Image
                    className="icon-settings"
                    src={user.icon}
                    width="400"
                    height="400"
                    alt="icon"
                  />
                ) : (
                  <Image
                    className="icon-settings"
                    src={iconDefault}
                    width="400"
                    height="400"
                    alt="iconDefault"
                  />
                )}
              </div>

              <div className="rainbow__button">
                <label htmlFor="icon">Edit profile picture</label>
                <input
                  type="file"
                  name="icon"
                  className="input-settings"
                  onChange={(e) => setFile(e.target.files?.[0] || null)}
                />
              </div>
            </div>
        
              {......}
              <div className="buttons">
                <button
                  className="buttons__addtask rainbow__button"
                  type="submit"
                >
                  Save changes
                </button>
                <hr></hr>
                <DeleteUser />
              </div>
            </div>
          </form>
        </div>
      </main>
    </div>
  );
};

export default Settings;

错误信息

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.cloudinary.com/v1_1/---/image/upload. (Reason: header ‘authorization’ is not allowed according to header ‘Access-Control-Allow-Headers’ from CORS preflight response).

XHRPOST https://api.cloudinary.com/v1_1/---/image/upload

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.cloudinary.com/v1_1/----/image/upload. (Reason: CORS request did not succeed). Status code: (null).

error uploading icon

已将Cloudinary上传预设设为"unsigned",之前曾因MongoDB用户文档缺少"icon"字段出现过类似问题,添加后恢复正常,且成功修改过几次头像,如今问题再次出现。


解决方案

1. 清除Cloudinary请求的Authorization头

问题核心是Axios可能全局配置了Authorization头,导致请求Cloudinary时自动带上该头,但Cloudinary的CORS策略不允许这个头。修改submitImage函数中的请求配置,显式清除headers:

const res = await axios.post(
  `https://api.cloudinary.com/v1_1/${cloud_name}/image/upload`,
  data,
  { headers: {} } // 重置headers,避免携带Authorization
);

2. 再次验证Cloudinary上传预设

  • 确认上传预设的模式为Unsigned,没有误切换为Signed。
  • 检查预设的CORS配置:如果设置了Allowed Origins,确保包含你的前端域名(比如http://localhost:3000或生产环境域名),若未设置则保持默认即可。

3. 优化Cookie更新方式

当前代码直接通过document.cookie修改用户Cookie,这种方式存在安全风险且可能导致缓存问题。建议由后端接口返回Set-Cookie头来更新用户Cookie,前端无需手动操作。

4. 增强错误排查

在submitImage的catch块中打印更详细的错误信息,方便定位问题:

catch (err) {
  console.log("error uploading icon", err.response?.data || err.message || err);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 06:35:22