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

AWS S3:curl PUT上传正常,但JavaScript fetch上传失败

S3预签名URL通过fetch PUT上传失败,但curl正常的排查方案

已经按常规方案在请求头添加Content-Type,后端生成预签名URL时也指定了ContentType,curl测试可以正常上传,但前端fetch上传失败,可从以下几个方向排查:

1. 排查fetch自动添加的额外请求头

浏览器的fetch会自动添加部分请求头(比如Accept、Origin),而S3预签名URL会严格校验所有请求头——只要请求中存在生成预签名时未声明的头,就会触发签名不匹配错误。

解决方法:

  • 通过浏览器开发者工具的Network面板,查看实际发送的所有请求头,确认是否有额外未预期的头;
  • 在后端生成预签名时,将这些额外头加入PutObjectCommand的Headers参数中,同时前端fetch显式设置这些头:
    后端代码修改:
    const command = new PutObjectCommand({
      Bucket: String(process.env.AWS_S3_ASSETS_BUCKET_NAME),
      Key: key,
      ContentType: mimetype,
      Headers: {
        'Accept': '*/*' // 匹配浏览器默认的Accept头
      }
    });
    
    前端代码修改:
    const res = await fetch(preSignedUrl, {
      method: 'PUT',
      headers: {
        'Content-Type': mimetype,
        'Accept': '*/*'
      },
      body: file
    });
    

2. 确认前后端使用的mimetype完全一致

前端file对象的type属性可能和你传给后端的mimetype不一致(比如部分浏览器中file.type为空,或返回格式与预期不同),导致预签名的ContentType与实际请求不匹配。

解决方法:

  • 前端打印console.log(mimetype, file.type),确认两个值完全相同;
  • 确保传给后端的mimetype直接取自file.type,避免手动拼接或转换导致的差异。

3. 检查S3桶的CORS配置

跨域场景下,浏览器会先发送OPTIONS预检请求,若S3桶的CORS规则未允许PUT请求或对应的头,预检会失败,导致后续PUT请求无法执行。

解决方法:
在S3控制台更新桶的CORS规则,允许PUT请求及所需头:

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>你的前端域名</AllowedOrigin> <!-- 如https://example.com,或用*允许所有(仅测试用) -->
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedHeader>Content-Type</AllowedHeader>
    <AllowedHeader>Accept</AllowedHeader>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
  </CORSRule>
</CORSConfiguration>

4. 尝试让浏览器自动设置Content-Type

手动设置的Content-Type可能和文件实际的MIME类型不匹配,可尝试去掉前端的Content-Type设置,让fetch根据file对象自动生成请求头。

代码示例:

const res = await fetch(preSignedUrl, {
  method: 'PUT',
  body: file
});

同时后端生成预签名时,要么不指定ContentType,要么确保和浏览器自动发送的一致。

5. 查看S3返回的具体错误信息

不要仅通过res.ok判断失败,打印完整的响应信息能精准定位问题(比如常见的SignatureDoesNotMatch错误)。

代码示例:

const res = await fetch(preSignedUrl, {
  method: 'PUT',
  headers: {
    'Content-Type': mimetype,
  },
  body: file
});
if (!res.ok) {
  const errorDetail = await res.text();
  console.error('上传失败详情:', res.status, res.statusText, errorDetail);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 22:10:24