当用户尝试将歌曲上传到我的 rails 6 应用程序时,收到来自 AWS S3 的 'Bad Request (400)' PUT 请求

Receiving a 'Bad Request (400)' on PUT request from AWS S3 when a user is trying to upload a song to my rails 6 app

我正在将我的 rails 5 应用程序升级到 rails 6。在此更新期间,我将我的 aws-sdk 从 v1 更新到 v3。如文档中所述,我已将 aws-sdk-s3 gem 包含在我的 gem 文件中。

我的应用程序的主要功能之一是允许用户上传歌曲。当我尝试手动测试此功能时遇到以下错误:

PUT https://bucketname.s3.amazonaws.com/ 400 (Bad Request)

附件XML:

<Error>
   <Code>AccessDenied</Code>
   <Message>Access Denied</Message>
   <RequestId>request id code</RequestId>  
   <HostId>host id code</HostId>
</Error>

我试图检查如何在此 S3 documentation 中解决它,但在那里我发现了另一个令人困惑的细节。虽然我的控制台返回了 400 状态代码,但 'AccessDenied' 实际上在文档中写为 403 状态代码。

我正在附加上传模型。注释掉的行是我原来的代码行。我根据S3文档中提到的内容进行了更改。

class Upload
  attr_reader :filename
  # New lines of code: ####################################################
  require 'aws-sdk-s3'

  Aws.config.update({
    region: 'us-east-1',
    credentials: Aws::Credentials.new(Rails.application.credentials.dig(:aws, :access_key_id), Rails.application.credentials.dig(:aws, :secret_access_key)),
  })
  #########################################################################
  def initialize(filename)
    @filename = filename
  end

  def url
    #@url ||= bucket_file.url_for(:write, content_type: content_type, acl: :public_read).to_s
    @url ||= bucket_file.url().to_s
  end

  def content_type
    @content_type ||= MIME::Types.type_for(filename).first.content_type
  end

  def to_json(*args)
    { url: url, content_type: content_type }.to_json
  end

  private
  
  def bucket_file
    #@bucket_file ||= bucket.object("uploads/#{SecureRandom.uuid}/#{filename}")
    @bucket_file ||= bucket.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public_read', content_type: content_type)
  end

  def bucket
    #@bucket ||= AWS::S3.new.buckets(ENV['S3_BUCKET_NAME'])  
    @bucket ||= Aws::S3::Resource.new(region: 'us-east-1').bucket(Rails.application.credentials.dig(:aws, :bucket))  
  end

end

值得一提的是,我已确保凭据可访问且有效。

url_for 函数已弃用,因此必须更改为 public_url

这是我的原始代码的工作版本:

class Upload
  attr_reader :filename
  require 'aws-sdk-s3'

  Aws.config.update({
    region: 'us-east-1',
    credentials: Aws::Credentials.new(Rails.application.credentials.dig(:aws, :access_key_id), Rails.application.credentials.dig(:aws, :secret_access_key)),
  })

  def initialize(filename)
    @filename = filename
  end

  def url
    @url ||= bucket_file.public_url().to_s
  end

  def content_type
    @content_type ||= MIME::Types.type_for(filename).first.content_type
  end

  def to_json(*args)
    { url: url, content_type: content_type }.to_json
  end

  private
  
  def bucket_file
    @bucket_file ||= bucket.object("uploads/#{SecureRandom.uuid}/${filename}")
  end

  def bucket 
    @bucket ||= Aws::S3::Resource.new(region: 'us-east-1').bucket(Rails.application.credentials.dig(:aws, :bucket))  
  end

end

处理过与您描述的类似的问题。

请注意,尽管您已解决此问题,但由于生产环境执行更严格的 csrf 令牌法规,因此在移至生产环境之前您需要进行进一步调整。

这里有进一步的解释:https://docs.aws.amazon.com/AmazonS3/latest/API/archive-RESTObjectPUT.html