如何允许向AWS S3存储桶发起POST请求?预签名上传遇405错误
问题描述
我尝试使用预签名POST请求将文件上传至AWS S3存储桶,Node.js服务器通过createPresignedPost生成预签名POST请求返回给React客户端,客户端借此上传用户生成的文件到S3存储桶。
目前客户端发起的所有POST请求均失败,返回405 Method Not Allowed。检查响应头发现Allow头未列出POST方法,但Access-Control-Allow-Methods头包含该方法。
还需要进行哪些配置才能允许向该存储桶发起POST请求?
注:选择预签名POST而非常规PUT预签名URL,是因为需要对文件类型和大小进行限制,后者不支持该功能。
上下文信息
该S3存储桶无桶策略,配置的CORS策略如下:
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "POST", "PUT", "HEAD", "DELETE" ], "AllowedOrigins": [ "http://localhost:3000", "http://localhost:8000", ... ], "ExposeHeaders": [ "ETag", "x-amz-meta-custom-header" ] } ]
服务端生成预签名POST请求代码
const signS3PostRequest = async ({ fileName, fileType, prefix, bucket }) => { const params = { Bucket: bucket, Fields: { $key, }, Expires: 60, ContentType: fileType, ACL: 'private', Conditions: [['content-length-range', 0, 10000000]], } const s3 = new aws.S3({ apiVersion: 'latest', region: $region, accessKeyId: process.env.AWS_ACCESSKEYID, secretAccessKey: process.env.AWS_SECRETACCESSKEY, }) return new Promise((resolve, reject) => { s3.createPresignedPost(params, (err, data) => { if (err) { ... } else { resolve(data) } }) }) }
客户端上传文件代码
export const uploadFileToS3UsingSignedPostRequest = ( file, signedRequestData ) => { let form = new FormData() Object.keys(signedRequestData.fields).forEach((k) => form.append(k, signedRequestData.fields[k]) ) form.append('file', file) return fetch(signedRequestData.url, { method: 'POST', body: form }) }
补充信息
s3.createPresignedPost的返回结果示例:
{ "url": "https://s3.amazonaws.com/$BUCKET_NAME", "fields": { "key": "test/6zb3rewm8cjydpslti3g/test.jpeg", "bucket": "$BUCKET_NAME", "X-Amz-Algorithm": "AWS4-HMAC-SHA256", "X-Amz-Credential": "$CREDENTIAL", "X-Amz-Date": "20221230T090441Z", "Policy": "eyJleHBpcmF0aW9uIjoiMjAyMi0xMi0zMFQwOTowNTo0MVoiLCJjb25kaXRpb25zIjp...", "X-Amz-Signature": "$SIGNATURE" } }
S3返回的错误响应
<?xml version="1.0" encoding="UTF-8"?> <Error> <Code>MethodNotAllowed</Code> <Message>The specified method is not allowed against this resource.</Message> <Method>POST</Method> <ResourceType>OBJECT</ResourceType> <RequestId>HTYWNACA6A1J6BBS</RequestId> <HostId>BSV4mFijEfgZEiMu6nVxqP9aocuhAwN7et/UfrO4r1PmkeFZ8nVUsNmcLVZoWCe06zrEFJXDfsE=</HostId> </Error>
解决方案
针对405错误,可从以下几个方向排查修复:
检查静态网站托管配置
如果存储桶开启了静态网站托管,S3会将POST请求视为针对静态资源的请求,而该模式不支持POST方法。建议关闭静态网站托管后重试,或确保预签名POST请求的目标URL为S3的REST API端点(而非静态网站托管域名)。修正预签名POST参数配置
Fields中的$key变量未正确赋值,需结合前缀和文件名生成完整对象键:Fields: { key: `${prefix}/${fileName}` }- 将
ContentType加入Conditions,确保上传文件类型符合限制:Conditions: [ ['content-length-range', 0, 10000000], ['eq', '$Content-Type', fileType] ] - 将
ACL配置同步到Fields或Conditions中,避免签名策略与表单字段不匹配:// 方式1:加入Fields Fields: { key: `${prefix}/${fileName}`, acl: 'private' } // 方式2:加入Conditions Conditions: [ ['content-length-range', 0, 10000000], ['eq', '$Content-Type', fileType], ['eq', '$acl', 'private'] ]
验证CORS策略生效状态
通过AWS CLI命令确认CORS配置已正确保存并生效:
aws s3api get-bucket-cors --bucket $BUCKET_NAME
- 检查IAM用户权限
生成预签名POST的IAM用户需拥有s3:PutObject权限,确保其IAM策略包含:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::$BUCKET_NAME/*" } ] }
内容的提问来源于stack exchange,提问作者Max Taylor-Davies
相关产品推荐
相关产品推荐

