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

无法通过Node后端生成的预签名URL向S3存储桶上传PDF

S3预签名URL上传PDF时的CORS与重定向问题排查

场景

  • 客户端(Next.js客户端组件)向后端(Next.js REST接口)请求预签名URL
  • 后端通过getSignedUrl生成S3预签名URL,代码如下:
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { env } from "@/env";

const AWS_REGION = "ap-southeast-2";

const s3Client = new S3Client({
  region: AWS_REGION,
  credentials: {
    accessKeyId: env.AWS_S3_ACCESS_ID,
    secretAccessKey: env.AWS_S3_ACCESS_KEY,
  },
});

const command = new PutObjectCommand({
     Bucket: s3Object.bucket,
     Key: s3Object.key,
     // 尝试过传入ContentType,但无效果
     // ContentType: s3Object.contentType,
});

// 根据文件大小等计算过期时间
const expiresIn = calculateExpirationTime(s3Object);

const presignedUrl = await getSignedUrl(s3Client, command, { expiresIn });

return presignedUrl;
  • 客户端接收预签名URL后,发送文件分片:
// 上传分片到预签名URL
const uploadPart = (opts: {
  url: string;
  chunk: Blob;
  contentType: string;
  chunkSize: number;
  fileName: string;
  maxRetries: number;
}) =>
  fetch(opts.url, {
    method: "PUT",
    body: opts.chunk,
    // 尝试过设置Content-Type,无效果
    //headers: {
    //  "Content-Type": opts.contentType,
    //},
  })

错误信息

前端抛出CORS错误:

Access to fetch at 'https://mybucket.s3.ap-southeast-2.amazonaws.com/mydoc.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AMZ_VALUE%2Fap-southeast-2%2Fs3%2Faws4_request&X-Amz-Date=20240516T010037Z&X-Amz-Expires=14400&X-Amz-Signature=SIGNATURE&X-Amz-SignedHeaders=host&x-id=PutObject' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

OPTIONS请求返回403 Forbidden:

Request Method: OPTIONS
Status Code: 403 Forbidden
Remote Address: :443
Referrer Policy: strict-origin-when-cross-origin

响应头:

Content-Type: application/xml
Date: Thu, 16 May 2024 01:03:33 GMT
Server: AmazonS3
Transfer-Encoding: chunked
X-Amz-Id-2: <ID>
X-Amz-Request-Id: <ID>

请求头:

Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Cache-Control: no-cache
Connection: keep-alive
Host: mybucket.s3.ap-southeast-2.amazonaws.com
Origin: http://localhost:3000
Pragma: no-cache
Referer: http://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: <AGENT>

存储桶与权限配置

当前S3存储桶CORS配置:

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "DELETE",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ]
    }
]

生成预签名URL的用户关联策略:

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": [
          "s3:GetObject",
          "s3:GetObjectVersion",
          "s3:PutObject"
        ],
        "Resource": [
          "arn:aws:s3:::mybucket",
          "arn:aws:s3:::bmybucket/*"
        ],
        "Effect": "Allow"
      }
    ]
}

更新信息

用Postman测试PUT请求时返回永久重定向错误:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>PermanentRedirect</Code>
    <Message>The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.</Message>
    <Endpoint>s3.amazonaws.com</Endpoint>
    <Bucket>mybucket</Bucket>
    <RequestId>__ID__</RequestId>
    <HostId>__ID__</HostId>
</Error>

解决方案

1. 修复用户策略中的资源ARN笔误

用户策略里的Resource存在拼写错误:arn:aws:s3:::bmybucket/*多了一个字母b,应改为arn:aws:s3:::mybucket/*。这个错误会导致用户没有目标桶的写入权限,S3处理OPTIONS预请求时返回403且不返回CORS响应头,最终触发前端CORS错误。

修正后的策略:

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": [
          "s3:GetObject",
          "s3:GetObjectVersion",
          "s3:PutObject"
        ],
        "Resource": [
          "arn:aws:s3:::mybucket",
          "arn:aws:s3:::mybucket/*"
        ],
        "Effect": "Allow"
      }
    ]
}

2. 修正S3桶的区域配置

Postman返回的PermanentRedirect错误表明,当前使用的区域(ap-southeast-2)与桶实际创建区域不匹配,桶实际应为us-east-1(错误指定的端点s3.amazonaws.com对应该区域)。

  • 登录AWS控制台,查看S3桶属性页面确认实际区域
  • 同步后端S3客户端的区域配置:若桶在us-east-1,则将AWS_REGION改为"us-east-1";若桶确实在ap-southeast-2,则检查桶命名是否符合区域端点规则(如旧式桶名需使用全局端点)

3. 确保预签名URL与请求头一致

生成PutObjectCommand时明确指定ContentType,同时恢复前端请求的对应头:

// 后端代码
const command = new PutObjectCommand({
     Bucket: s3Object.bucket,
     Key: s3Object.key,
     ContentType: s3Object.contentType,
});
// 前端代码
fetch(opts.url, {
  method: "PUT",
  body: opts.chunk,
  headers: {
    "Content-Type": opts.contentType,
  },
})

4. 验证CORS配置生效

修正权限和区域后,重新测试OPTIONS请求,确认响应头包含Access-Control-Allow-Origin等CORS字段。若仍不生效,可重新保存CORS配置确保AWS应用最新设置。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:44:52