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

Rails 7.2.3开启public:true后无法写入S3兼容存储桶

Rails 7.2.3 向S3兼容存储桶写入失败的解决方案

问题场景

使用Rails 7.2.3向Linode对象存储(S3兼容)上传文件时,执行以下代码:

Users::User.last.avatar.attach(io: img_io, ...)

触发错误:

(railsapp):4:in '<main>': A header (x-amz-acl) that you provided implies functionality that is not implemented. (Aws::S3::Errors::NotImplemented)

需求:

  • 存储桶禁止他人写入/删除,但允许所有人无需签名URL即可读取文件
  • 关闭public: true时可正常写入,但生成的Blob URL是带过期时间的签名URL,不符合公开访问需求

当前配置与存储桶信息

storage.yml配置

bucketname:
  service: S3
  endpoint: https://region.linodeobjects.com
  access_key_id: access_key
  secret_access_key: secret_access_key
  region: default
  bucket: bucketname
  public: true

存储桶权限信息(s3cmd输出)

# s3cmd info s3://bucketname:

   Location:  default
   Payer:     none
   Ownership: none
   Versioning:none
   Expiration rule: none
   Block Public Access: none
   Policy:    {
  "Version": "2012-10-17",
  "Statement": [
    {
        "Sid": "PublicReadGetObject",
        "Effect": "Allow",
        "Principal": {
            "AWS": "*"
        },
        "Action": "s3:GetObject",
        "Resource": [
        "arn:aws:s3:::bucketname",
        "arn:aws:s3:::bucketname/*"
      ]
    },    
    {
      "Sid": "auto-1234",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam:::user/blablah"
      },
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:DeleteBucketWebsite",
        "s3:DeleteObject",
        "s3:DeleteObjectTagging",
        "s3:DeleteObjectVersion",
        "s3:DeleteObjectVersionTagging",
        "s3:DeleteReplicationConfiguration",
        "s3:PutBucketCORS",
        "s3:PutBucketLogging",
        "s3:PutBucketNotification",
        "s3:PutBucketTagging",
        "s3:PutBucketVersioning",
        "s3:PutBucketWebsite",
        "s3:PutLifecycleConfiguration",
        "s3:PutObject",
        "s3:PutObjectTagging",
        "s3:PutObjectAcl",
        "s3:PutObjectVersionAcl",
        "s3:PutObjectVersionTagging",
        "s3:PutReplicationConfiguration",
        "s3:RestoreObject",
        "s3:GetBucketAcl",
        "s3:GetBucketCORS",
        "s3:GetBucketLocation",
        "s3:GetBucketLogging",
        "s3:GetBucketNotification",
        "s3:GetBucketPolicy",
        "s3:GetBucketTagging",
        "s3:GetBucketVersioning",
        "s3:GetBucketWebsite",
        "s3:GetLifecycleConfiguration",
        "s3:GetObjectAcl",
        "s3:GetObject",
        "s3:GetObjectTagging",
        "s3:GetObjectTorrent",
        "s3:GetReplicationConfiguration",
        "s3:GetObjectVersionAcl",
        "s3:GetObjectVersion",
        "s3:GetObjectVersionTagging",
        "s3:GetObjectVersionTorrent",
        "s3:ListBucketMultipartUploads",
        "s3:ListBucket",
        "s3:ListBucketVersions",
        "s3:ListMultipartUploadParts"
      ],
      "Resource": [
        "arn:aws:s3:::bucketname",
        "arn:aws:s3:::bucketname/*"
      ]
    }
  ]
}

   CORS:      none
   ACL:       *anon*: READ
   ACL:       blah: FULL_CONTROL
   URL:       http://bucketname.region.linodeobjects.com/

解决方案

核心原因

Linode对象存储不支持通过x-amz-acl请求头设置对象级ACL(你的存储桶已通过桶策略和ACL全局配置了公开读),但Rails的public: true配置会自动给上传请求添加x-amz-acl: public-read头,触发NotImplemented错误。

修改配置

更新storage.yml,禁用x-amz-acl头发送,同时配置生成无签名的公开URL:

bucketname:
  service: S3
  endpoint: https://region.linodeobjects.com
  access_key_id: access_key
  secret_access_key: secret_access_key
  region: default
  bucket: bucketname
  # 移除原有的public: true
  public: false
  # 禁止上传时发送x-amz-acl头
  upload_options:
    acl: nil
  # 配置URL生成时跳过签名,直接生成公开URL
  url_options:
    skip_signature: true

验证效果

  1. 执行上传代码不再触发Aws::S3::Errors::NotImplemented错误
  2. 生成的Blob URL为无签名的公开地址(如http://bucketname.region.linodeobjects.com/xxx/xxx/file.jpg),所有人可直接访问
  3. 存储桶权限保持原有策略:仅指定IAM用户拥有写/删除权限,其他用户仅能读取文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 21:17:26