使用 CarrierWave 通过 AWS S3上传文件到阿里云 OSS

虽然阿里云 OSS 与 AWS S3 兼容,但需要使用阿里云的特定端点进行配置。CarrierWave 是一个流行的 Ruby 文件上传库,可以方便地与 AWS S3 集成。以下是配置和使用方法:

1. 安装必要的 gem

首先,在 Gemfile 中添加以下 gem:

ruby 复制代码
gem 'carrierwave'
gem 'aws-sdk-s3' 

然后运行 bundle install

2. 配置 CarrierWave

创建上传器

运行生成器创建上传器:

bash 复制代码
rails generate uploader Avatar

配置 AWS S3

config/initializers/carrierwave.rb 中添加以下配置:

ruby 复制代码
CarrierWave.configure do |config|  
  config.fog_credentials = {
    provider:              'AWS',                        # 必填
    aws_access_key_id:     ENV['AWS_ACCESS_KEY_ID'],     # 推荐使用环境变量
    aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], # 推荐使用环境变量
    region:                'cn-hangzhou',                # 如 'us-east-1'
    host:                  ENV['AWS_HOST'],              # 可选,自定义端点
    endpoint:              ENV['AWS_ENDPOINT'],         # 可选,用于兼容S3的服务
    path_style:            false,                        # 设为 false 使用虚拟托管式访问
    aws_signature_version: 4
  }
  config.fog_directory  = ENV['AWS_BUCKET']             # 存储桶名称
  config.fog_public     = false                         # 默认文件权限
  config.fog_attributes = { 'Cache-Control' => "max-age=#{365.day.to_i}" }
  
  # 可选:使用存储桶的子目录
  config.fog_path_prefix = "uploads"
end

3. 在模型中使用上传器

假设你有一个 User 模型需要上传头像:

ruby 复制代码
class User < ApplicationRecord
  mount_uploader :avatar, AvatarUploader
end

4. 上传器配置示例

app/uploaders/avatar_uploader.rb 中:

ruby 复制代码
class AvatarUploader < CarrierWave::Uploader::Base
  # 选择存储类型
  storage :fog  # 使用 AWS S3
  
  # 覆盖存储目录
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # 添加白名单扩展名
  def extension_whitelist
    %w(jpg jpeg gif png)
  end

  # 覆盖文件名
  def filename
    "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) || model.instance_variable_set(var, SecureRandom.uuid)
  end
end

5. 在视图中使用

表单中使用文件字段:

ruby 复制代码
<%= form_for @user do |f| %>
  <%= f.file_field :avatar %>
  <%= f.submit %>
<% end %>

显示上传的文件:

ruby 复制代码
<%= image_tag @user.avatar.url if @user.avatar? %>

6. 处理大文件

对于大文件上传,你可能需要:

ruby 复制代码
# 在 config/initializers/carrierwave.rb 中
config.fog_attributes = {
  'x-amz-meta-uploaded-by' => 'my-app',
  'Cache-Control' => 'max-age=315576000'
}

7. 测试环境配置

在测试环境中,你可能想使用本地存储:

ruby 复制代码
# config/environments/test.rb
config.carrierwave.storage = :file
config.carrierwave.enable_processing = false

解决 AWS SDK 上传到阿里云 OSS 的 403 Forbidden 错误

你遇到的 SignatureDoesNotMatch 错误表明 AWS SDK 生成的签名与阿里云 OSS 期望的签名不匹配。这是使用 AWS SDK 访问阿里云 OSS 时的常见问题,因为阿里云 OSS 虽然兼容 S3 协议,但在签名计算上有一些差异。

增加 CarrierWave 配置

ruby 复制代码
# 在 config/initializers/carrierwave.rb 中
config.fog_credentials = {
  #...
  path_style:            true,  # 注意这里是 path_style 而不是 force_path_style
  aws_signature_version: 4      # 注意参数名是 aws_signature_version 而不是 signature_version
}

但是还是不能完美解决阿里云 OSS 的 SignatureDoesNotMatch 问题。

当您在 CarrierWave 配置中添加:

复制代码
aws_signature_version: 4

时,fog-aws 内部仍然会强制使用 service: 's3',这就是导致签名不匹配的根本原因。

解决方案:

如果您必须使用 fog-aws,可以通过猴子补丁修改:

ruby 复制代码
# 在 config/initializers/carrierwave.rb 中添加以下补丁
module Fog
  module AWS
    class SignatureV4
      # 重写初始化方法,强制指定service为'oss'
      def initialize(aws_access_key_id, secret_key, region, service)
        @region = region
        @service = 'oss'  # 强制覆盖为oss,原参数service被忽略
        @aws_access_key_id = aws_access_key_id
        @hmac = Fog::HMAC.new('sha256', 'AWS4' + secret_key)
      end
    end
  end
end
相关推荐
测试运维日常笔记2 天前
AWS EKS 集群部署与清理手顺
云计算·aws
故乡dee云3 天前
AWS 产品太多不会选?按“网站、数据库、文件、日志”4 类需求快速匹配
数据库·云计算·aws
吴声子夜歌3 天前
正则指引——Ruby
开发语言·正则表达式·ruby
霸道流氓气质5 天前
RabbitMQ 延迟队列与死信队列:原理、实现与实战
分布式·rabbitmq·ruby
翼龙云_cloud5 天前
亚马逊云代理商:VPC网络ACL和安全组配置实践 从网络层到实例层的双重防护
运维·网络·安全·云计算·aws
Freed&9 天前
阿里云 OSS 至 AWS S3 跨云迁移实施指南
阿里云·云计算·aws
故乡dee云11 天前
AWS CloudFront 使用教程:CDN 加速功能和网站部署优化
云计算·aws
国际云,接待15 天前
AWS RDS 备份与 PITR 恢复演练:从保留期、恢复命令到应用切换验证
运维·云计算·aws·灾难恢复·数据库备份·rds
国际云,接待17 天前
AWS账单防爆雷实战:Budgets、Cost Anomaly Detection与预算动作联动
人工智能·aws·twitter·finops·云成本·budgets
故乡dee云17 天前
AWS 月度账单怎么看?费用明细、扣款时间和结算规则说明
云计算·aws