使用 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
相关推荐
yyuuuzz2 天前
谷歌云使用的几个常见注意事项
运维·服务器·网络·安全·web安全·云计算·aws
zhojiew2 天前
在AWS中国区的EMR集群中实现基于向量语义搜索的HBase运维诊断系统
运维·hbase·aws
yyuuuzz2 天前
独立开发者线上服务运维的几点实践经验
运维·服务器·网络·云计算·aws
zhojiew2 天前
使用DBT(data build tool)集成AWS Athena完成数据处理的实践
云计算·aws
yyuuuzz3 天前
aws的核心概念与常见使用场景
运维·服务器·网络·云计算·aws
zhojiew4 天前
在AWS云上使用EC2 嵌套虚拟化实例部署Cube Sandbox的实践和问题
云计算·aws
hhb_6184 天前
Ruby核心技术难点梳理与实战应用案例解析
服务器·前端·ruby
heimeiyingwang4 天前
【架构实战】RabbitMQ实战:企业级消息可靠传递
架构·rabbitmq·ruby
yyuuuzz5 天前
国际云服务器的技术特点与使用经验
运维·服务器·网络·数据库·云计算·aws
西洼工作室5 天前
前端直传OSS服务端签名(Policy+Signature)/STS临时凭证
前端·文件上传·oss