无法通过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
相关产品推荐
相关产品推荐

