如何通过CarrierWave获取AWS S3私有存储桶对象访问URL
问题描述
我在Rails应用中使用CarrierWave将文件上传到策略为"Bucket and Object are not public"的AWS S3私有存储桶。但在尝试通过@document.pdf_file.url生成私有桶内对象的访问URL时,出现了**"bucket_name is required"错误;当我尝试传入桶名改为@document.pdf_file.url 'privatebucketname'时,又出现"ArgumentError: Version privatebucketname doesn't exist!"**错误。
我的上传器代码如下:
class SecureUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick storage :fog def fog_directory ENV['AWS_SECURED_BUCKET'] end def fog_public false end def fog_authenticated_url_expiration 1.minutes # in seconds from now, (default is 10.minutes) end end
CarrierWave全局配置:
CarrierWave.configure do |config| config.fog_credentials = { provider: 'AWS', aws_access_key_id: ENV['AWS_KEY'], aws_secret_access_key: ENV['AWS_SECRET'], region: ENV['AWS_REGION'] } # For testing, upload files to local `tmp` folder. if Rails.env.test? || Rails.env.cucumber? config.storage = :file config.enable_processing = false config.root = "#{Rails.root}/tmp" else config.storage = :fog end config.cache_dir = "#{Rails.root}/tmp/uploads" # To let CarrierWave work on Heroku config.fog_directory = ENV['AWS_BUCKET'] end
问题分析与解决方案
为什么会报错?
首先得澄清一个误区:url方法的参数不是桶名——CarrierWave的url方法如果传参数,是用来指定你定义的不同尺寸的图片版本,而非存储桶名称,这就是你第二个错误的直接原因。而第一个"bucket_name is required"错误,大概率是因为CarrierWave在生成签名URL时,没有正确读取到私有桶的配置。
正确解决步骤:
1. 用authenticated_url替代url生成私有对象链接
因为你的桶是私有的,url方法默认会尝试生成公共URL,但私有对象没有公共访问权限,所以必须调用**authenticated_url**方法来生成带签名的临时访问链接。这个方法会自动使用你上传器中配置的fog_authenticated_url_expiration设置过期时间:
# 生成1分钟后过期的签名URL(使用上传器配置的过期时间) @document.pdf_file.authenticated_url
如果需要自定义过期时间,也可以直接传参数覆盖上传器的设置:
# 生成5分钟后过期的签名URL @document.pdf_file.authenticated_url(expires_in: 5.minutes)
2. 确认上传器配置正确覆盖全局设置
你的SecureUploader已经通过fog_directory指向了私有桶ENV['AWS_SECURED_BUCKET'],这一步是正确的,但要确保这个环境变量确实已正确设置,且在非测试环境下生效。可以在控制台打印验证:
# 确认桶名是否正确 @document.pdf_file.fog_directory # 确认是否为私有设置 @document.pdf_file.fog_public # 应该返回false
3. 检查AWS权限
确保你配置的AWS密钥拥有该私有桶的s3:GetObject权限,否则即使生成了签名URL,访问时也会返回权限错误。
内容的提问来源于stack exchange,提问作者Anjan

