简体   繁体   中英

Using carrierwave to upload one image to multiple storage location

I would like to be able to upload one image into two different locations: one location would be on the local filesystem (of the server) and the other would be Amazon S3 (the Amazon S3 location would be optional).

My current environment is Rails 3.2.8, Ruby 1.9.3, with Carrierwave used for uploading the file.

I've had some success using the following method:

Model

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

Relevant view form fields (simple form syntax)

<p><%= f.input :image, as: :file %></p>
<p><%= f.input :remote, as: :boolean %></p>

The user checks the "remote" checkbox in the form and chooses the image to upload. The before_save callback stores a duplicate of image into image_remote, the file is processed by their respective uploaders, and I have my desired result.

However, I'm starting to run into problems when I want to update that field. For example, if the user chooses to first upload the file locally and not to S3 (does not check the remote checkbox), then later comes back to the form and checks the remote checkbox. In this case, the before_save callback does not get run because no real active record column has been changed (only the remote flag). I've tried to use before_validation, but this fails to work (the image_remote uploader stores the proper filename in the image_remote column, but the image does not get uploaded to S3). Obviously something is changing between the before_validation and the before_save (image attribute is being converted to and uploader?) but I can't seem to figure out why this doesn't work.

With all this being said, I think my approach with using dup is a bit of a hack, and I'm hoping someone can advise me in a more elegant way of reaching my goal.

Thanks for your help.

I was to solve this, although I'm still not sure if it's the most elegant solution.

First off, I mentioned in my question that when I registered config_for_remote_upload with the before_validation callback, the file was not uploaded to S3, but the image_remote column was populated. Upon further inspection, the situation is even worse. When initializing the image_remote uploader within the before_validation callback, all files were deleted on the S3 storage bucket! I replicated this a couple times. I only tested when the store_dir was set to nil in the uploaded, thus putting the files at the root of the bucket.

Initializing the image_remote column in during the before_save callback does not have this problem. In order force the record to save (it wouldn't save, because only a non db column attribute was being changed) I added a before_validation that changed the update_at field of the record.

before_validation: :change_record_updated_at
...
def change_record_updated_at
   self.update_at=Time.current
end

I also moved away from using dup, not because it didn't work, but rather because I didn't know why it worked. Instead I created a StringIO object for the file and assigned that to the image_remote column.

  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

See here for further info on FilelessIO (StringIO with original_filename).

With this configuration, the file can be uploaded to the second storage location (S3 in my case) after the initial upload.

Hope this helps someone else out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM