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

使用Next.js客户端上传至GCloud签名URL时出现403 Forbidden错误

GCS签名URL前端上传403问题排查与修复

问题根源分析

  1. Content-Type不匹配:签名URL生成时若指定了Content-Type约束,前端必须完全匹配该值(包括大小写、参数细节);若生成时未指定,前端手动设置Content-Type会触发签名校验失败。
  2. 错误的请求体格式:纯JS代码中使用FormData作为PUT请求体,会自动将Content-Type改为multipart/form-data,与签名URL预期的文件原生MIME类型不符。
  3. 响应解析错误:GCS的PUT成功响应为空,前端尝试解析JSON会抛出不必要的错误。

修复方案

1. 调整Next.js上传代码

移除手动设置的Content-Type,直接传递文件对象,并修正响应处理逻辑:

"use client"
import { getSignedURLs } from "../../upload/upload"

export default function Images(props) {
  const submitImages = async (e) => {
    e.preventDefault()
    const form = e.currentTarget
    const data = new FormData(form)
    const file = data.get("file")

    try {
      const signedUrls = await getSignedURLs(1)
      const signedUrl = signedUrls[0]
      
      const response = await fetch(signedUrl, {
        method: "PUT",
        body: file
        // 不手动设置Content-Type,由浏览器自动匹配文件类型
      })

      if (response.ok) {
        console.log("文件上传成功")
        // 此处处理上传成功后的业务逻辑
      } else {
        const errorDetails = await response.text()
        throw new Error(`上传失败: ${response.status} - ${errorDetails}`)
      }
    } catch (error) {
      console.error("上传出错:", error)
    }
  }

  return (
    <form id="uploadForm" onSubmit={submitImages}>
      <input type="file" name="file" />
      <button type="submit">Upload</button>
    </form>
  )
}

2. 修复纯JS上传代码

移除FormData包装,直接传递文件对象:

document.getElementById("uploadForm").addEventListener("submit", async function (event) {
  event.preventDefault();
  const file = this.file.files[0];

  try {
    const signedUrls = await getSignedURLs(1);
    const signedUrl = signedUrls[0];
    
    const response = await fetch(signedUrl, {
      method: "PUT",
      body: file
      // 不设置Content-Type
    });

    if (response.ok) {
      console.log("文件上传成功");
    } else {
      const errorDetails = await response.text();
      throw new Error(`上传失败: ${response.status} - ${errorDetails}`);
    }
  } catch (error) {
    console.error("上传出错:", error);
  }
});

3. 后端签名URL生成校验

  • 若不需要限制上传文件类型,生成签名URL时不要添加Content-Type条件;
  • 若需要限制,确保前端实际发送的Content-Type与生成时指定的值完全一致(比如image/jpeg不能写成image/JPEG);
  • 检查签名URL的有效期,避免上传时已过期。

4. 存储桶CORS配置检查

确保存储桶允许当前域名的PUT请求,示例CORS配置:

[
  {
    "origin": ["https://你的Next.js域名"],
    "method": ["PUT"],
    "responseHeader": ["Content-Type"],
    "maxAgeSeconds": 3600
  }
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:37:37