我希望能够将一个图像上传到两个不同的位置:一个位置在(服务器的)本地文件系统上,另一个位置是Amazon S3(Amazon S3位置是可选的)。
我目前的环境是Rails 3.2.8,Ruby 1.9.3,Carrierwave用于上传文件。
我使用以下方法取得了一些成功:
模型
class Image < ActiveRecord:Base
attt_accessor :remote
before_save :configure_for_remote
mount_uploader :image, ImageUploader #stores images locally
mount_uploader :image_remote, ImageRemoteUploader #store images on S3
def configure_for_remote
if self.remote=="1"
self.image_remote = self.image.dup
end
end
end
相关视图表单字段(简单表单语法)
<p><%= f.input :image, as: :file %></p>
<p><%= f.input :remote, as: :boolean %></p>
用户选中表单中的“远程”复选框,然后选择要上传的图像。 before_save回调将图像副本存储到image_remote中,文件由各自的上传者处理,我得到了我想要的结果。
但是,当我想要更新该字段时,我开始遇到问题。例如,如果用户选择首先在本地上载文件而不是S3(不检查远程复选框),则稍后返回到表单并检查远程复选框。在这种情况下,before_save回调不会运行,因为没有更改真正的活动记录列(只有远程标志)。我尝试使用before_validation,但是这不起作用(image_remote上传器在image_remote列中存储了正确的文件名,但图像没有上传到S3)。显然,before_validation和before_save之间正在发生变化(图像属性正在被转换为上传者?)但我似乎无法弄清楚为什么这不起作用。
说了这么多,我认为我使用dup
的做法有点像黑客,我希望有人能以更优雅的方式告诉我达到我的目标。
感谢您的帮助。
答案 0 :(得分:1)
我是要解决这个问题,虽然我仍然不确定它是否是最优雅的解决方案。
首先,我在我的问题中提到,当我使用before_validation回调注册config_for_remote_upload时,文件未上传到S3,但是填充了image_remote列。经过进一步检查,情况更糟。在before_validation回调中初始化image_remote上传器时,S3存储桶上的所有文件都被删除了!我复制了几次。我只测试了在上传时将store_dir设置为nil,从而将文件放在存储桶的根目录。
在before_save回调期间初始化image_remote列没有此问题。为了强制记录保存(它不会保存,因为只更改了非db列属性)我添加了一个更改记录的update_at字段的before_validation。
before_validation: :change_record_updated_at
...
def change_record_updated_at
self.update_at=Time.current
end
我也不再使用dup,不是因为它不起作用,而是因为我不知道它为什么有效。相反,我为该文件创建了一个StringIO对象,并将其分配给image_remote列。
def config_for_remote_upload
if self.remote.to_i==1
#self.image_remote = self.image.dup
#this will open the file as binary
img_binary = File.open(self.image.file.path){ |i| i.read }
img_encoded = Base64.encode64(img_binary)
io = FilelessIO.new(Base64.decode64(img_encoded))
io.original_filename = self.image.file.original_filename
self.image_remote = io
elsif self.remote.to_i==0
#delete remote image and clear field
self.remove_image_remote = true
end
end
有关FilelessIO(带有original_filename的StringIO)的详细信息,请参阅here。
使用此配置,初始上传后,文件可以上传到第二个存储位置(在我的情况下为S3)。
希望这有助于其他人。