3

1 つのイメージを 2 つの異なる場所にアップロードできるようにしたいと考えています。1 つの場所は (サーバーの) ローカル ファイルシステム上にあり、もう 1 つは 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

私はこれを解決することでしたが、それが最もエレガントな解決策であるかどうかはまだわかりません。

まず、質問の中で、config_for_remote_uploadをbefore_validationコールバックに登録したときに、ファイルが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が機能しなかったためではなく、なぜ機能したのかわからなかったため、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)の詳細については、ここを参照してください。

この構成では、最初のアップロード後にファイルを2番目の保存場所(私の場合はS3)にアップロードできます。

これが他の誰かを助けることを願っています。

于 2012-10-18T15:22:42.163 に答える