1

Any idea how to migrate a running project using Refile to the new rails's Active Storage?

Anyone knows any tutorial/guide about how to do that?

Thanks, Patrick

alispat
  • 45
  • 2
  • 5

1 Answers1

1

I wrote a short post about it here which explains the process in detail: https://dev.to/mtrolle/migrating-from-refile-to-activestorage-2dfp

Historically I hosted my Refile attached files in AWS S3, so what I did was refactoring all my code to use ActiveStorage instead. This primarily involved updating my model and views to use ActiveStorage syntax.

Then I removed the Refile gem and replaced it with ActiveStorage required gems like the image_processing gem and the aws-sdk-s3 gem.

Finally I created a Rails DB migration file to handle the actual migration of existing files. Here I looped through all records in my model with a Refile attachment to find their respective file in AWS S3, download it and then attach it to the model again using the ActiveStorage attachment.

Once the files were moved I could remove the legacy Refile database fields:

require 'mini_magick' # included by the image_processing gem
require 'aws-sdk-s3' # included by the aws-sdk-s3 gem

class User < ActiveRecord::Base
  has_one_attached :avatar
end

class MovingFromRefileToActiveStorage < ActiveRecord::Migration[6.0]
  def up
    puts 'Connecting to AWS S3'
    s3_client = Aws::S3::Client.new(
      access_key_id: ENV['AWS_S3_ACCESS_KEY'],
      secret_access_key: ENV['AWS_S3_SECRET'],
      region: ENV['AWS_S3_REGION']
    )

    puts 'Migrating user avatar images from Refile to ActiveStorage'
    User.where.not(avatar_id: nil).find_each do |user|
      tmp_file = Tempfile.new

      # Read S3 object to our tmp_file
      s3_client.get_object(
        response_target: tmp_file.path,
        bucket: ENV['AWS_S3_BUCKET'],
        key: "store/#{user.avatar_id}"
      )

      # Find content_type of S3 file using ImageMagick
      # If you've been smart enough to save :avatar_content_type with Refile, you can use this value instead
      content_type = MiniMagick::Image.new(tmp_file.path).mime_type

      # Attach tmp file to our User as an ActiveStorage attachment
      user.avatar.attach(
        io: tmp_file,
        filename: "avatar.#{content_type.split('/').last}",
        content_type: content_type
      )

      if user.avatar.attached?
        user.save # Save our changes to the user
        puts "- migrated #{user.try(:name)}'s avatar image."
      else
        puts "- \e[31mFailed to migrate the avatar image for user ##{user.id} with Refile id #{user.avatar_id}\e[0m"
      end

      tmp_file.close
    end

    # Now remove the actual Refile column
    remove_column :users, :avatar_id, :string
    # If you've created other Refile fields like *_content_type, you can safely remove those as well
    # remove_column :users, :avatar_content_type, :string
  end

  def down
    raise ActiveRecord::IrreversibleMigration
  end
end
mtrolle
  • 2,234
  • 20
  • 19