3

我希望能够将一张图像上传到两个不同的位置:一个位置在(服务器的)本地文件系统上,另一个位置是 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有点像黑客,我希望有人能以更优雅的方式为我提供建议,以实现我的目标。

谢谢你的帮助。

4

1 回答 1

1

我要解决这个问题,尽管我仍然不确定它是否是最优雅的解决方案。

首先,我在我的问题中提到,当我使用 before_validation 回调注册 config_for_remote_upload 时,文件没有上传到 S3,但填充了 image_remote 列。经进一步检查,情况更糟。在 before_validation 回调中初始化 image_remote 上传器时,S3 存储桶上的所有文件都被删除了!我复制了几次。我仅在上传时将 store_dir 设置为 nil 时进行了测试,从而将文件放在存储桶的根目录下。

在 before_save 回调期间初始化 image_remote 列没有这个问题。为了强制保存记录(它不会保存,因为只有一个非 db 列属性被更改)我添加了一个 before_validation 来更改记录的 update_at 字段。

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)的更多信息,请参见此处。

使用此配置,可以在初始上传后将文件上传到第二个存储位置(在我的情况下为 S3)。

希望这可以帮助其他人。

于 2012-10-18T15:22:42.163 回答