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

如何允许向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错误,可从以下几个方向排查修复:

  1. 检查静态网站托管配置
    如果存储桶开启了静态网站托管,S3会将POST请求视为针对静态资源的请求,而该模式不支持POST方法。建议关闭静态网站托管后重试,或确保预签名POST请求的目标URL为S3的REST API端点(而非静态网站托管域名)。

  2. 修正预签名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']
      ]
      
  3. 验证CORS策略生效状态
    通过AWS CLI命令确认CORS配置已正确保存并生效:

aws s3api get-bucket-cors --bucket $BUCKET_NAME
  1. 检查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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 02:01:12