Search code examples
ruby-on-railscarrierwaverails-activestorage

Migrating uploaded files from Active Storage to Carrierwave


For a variety of reasons I am migrating my uploads from ActiveStorage (AS) to CarrierWave (CW).

I am making rake task and have the logic sorted out - I am stumped at how to feed the AS blob into the CW file.

I am trying something like ths:

@files.each.with_index(1) do | a, index |

  if a.attachment.attached?

    a.attachment.download do |file|
      a.file = file
    end
    a.save!

  end

end      

This is based on these two links:

https://edgeguides.rubyonrails.org/active_storage_overview.html#downloading-files

message.video.open do |file|
  system '/path/to/virus/scanner', file.path
  # ...
end

and

https://github.com/carrierwaveuploader/carrierwave#activerecord

# like this
File.open('somewhere') do |f|
  u.avatar = f
end

I tested this locally and the files are not mounted via the uploader. My question(s) would be:

  • am I missing something obvious here?
  • is my approach wrong and needs a new one?

Bonus Karma Question:

  • I can't seem to see a clear path to set the CW filename when I do this?

Solution

  • The file object you get from the attachment.download block is a string. More precisely, the response from .download is the file, "streamed and yielded in chunks" (see documentation). I validated this by calling file.class to make sure the class is what I expected.

    So, to solve your issue, you need to provide an object on which .read can be called. Commonly that is done using the Ruby StringIO class.

    However, considering Carrierwave also expects a filename, you can solve it using a helper model that inherits StringIO (from blogpost linked above):

    class FileIO < StringIO
      def initialize(stream, filename)
        super(stream)
        @original_filename = filename
      end
    
      attr_reader :original_filename
    end
    

    And then you can replace a.file = file with a.file = FileIO.new(file, 'new_filename')