This tutorial allow you to migrate the Paperclip that uses Hash in the file name to Carrierwave. I will not give all the details but I will give the before and after at the end of this tutorial.
In my case the config of the upload with Paperclip was:
class SomeAttachment < ActiveRecord::Base
has_attached_file :attachment,
validate_media_type: false,
url: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
path: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
hash_secret: Rails.application.secrets.secret_key_base.to_s,
s3_permissions: :private
end
It was saving the raw file name at attachment_file_name
but to migrate to Carrierwave
and to access the file properly we need to save the file name with the generated hash (Paperclip does that).
To do that, I created a new column called attachment_file_name_with_hash
and ruuned this script:
SomeAttachment.find_each do |attachment|
attachment.update_column(:attachment_file_name_with_hash, attachment.attachment.path.split('/').last)
end
I created the new attachment column:
add_column :some_attachments, :attachment, :string
And migrated the values in a migration:
execute(<<-SQL)
update some_attachments
set attachment = attachment_file_name_with_hash;
SQL
Finaly when creating the Carrierwave uploader, don't forget to set the store_dir
equals to the Paperclip config.
In my case:
def store_dir
"#{Rails.env}/#{model.class.to_s.underscore.pluralize}/#{model.id}"
end
class AbsenceJustificationAttachment < ActiveRecord::Base
belongs_to :absence_justification
has_attached_file :attachment,
validate_media_type: false,
url: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
path: "/#{Rails.env}/:class/:id/:basename-:hash.:extension",
hash_secret: Rails.application.secrets.secret_key_base.to_s,
s3_permissions: :private
validates_attachment_file_name :attachment, matches: [/png\z/, /jpeg\z/, /jpg\z/, /gif\z/, /pdf\z/, /odt\z/,
/doc\z/, /docx\z/, /ods\z/, /xls\z/, /xlsx\z/, /odp\z/,
/ppt\z/, /pptx\z/, /odg\z/, /xml\z/, /csv\z/]
validates_with AttachmentSizeValidator, attributes: :attachment, less_than: 3.megabytes
validates :attachment, presence: true
end
class AbsenceJustificationAttachment < ActiveRecord::Base
belongs_to :absence_justification
mount_uploader :attachment, DocUploader
validates :attachment, presence: true
def filename
attachment&.path&.split('/')&.last
end
end
class DocUploader < CarrierWave::Uploader::Base
def store_dir
"#{Rails.env}/#{model.class.to_s.underscore.pluralize}/#{model.id}"
end
def extension_white_list
%w[png jpeg jpg gif pdf odt doc docx ods xls xlsx odp ppt pptx odg xml csv]
end
def filename
original = original_filename.split(".")[0..-2].join(".")
"#{original} - #{secure_token}.#{file.extension}" if original_filename.present?
end
protected
def secure_token
var = :"@#{mounted_as}_secure_token"
model.instance_variable_get(var) || model.instance_variable_set(var, SecureRandom.uuid)
end
end
You can specify you're coming from paperclip with something like this:
this would support paperclips way of defining upload and download locations, hence not breaking previous uploads.
See more here: https://www.rubydoc.info/github/jnicklas/carrierwave/CarrierWave/Compatibility/Paperclip