Is there a way to validate attachments with ActiveStorage? For example, if I want to validate the content type or the file size?
Something like Paperclip's approach would be great!
validates_attachment_content_type :logo, content_type: /\Aimage\/.*\Z/
validates_attachment_size :logo, less_than: 1.megabytes
Well, it ain't pretty, but this may be necessary until they bake in some validation:
validate :logo_validation
def logo_validation
if logo.attached?
if logo.blob.byte_size > 1000000
logo.purge
errors[:base] << 'Too big'
elsif !logo.blob.content_type.starts_with?('image/')
logo.purge
errors[:base] << 'Wrong format'
end
end
end
ActiveStorage doesn't support validations right now. According to https://github.com/rails/rails/issues/31656.
Update:
Rails 6 will support ActiveStorage validations.
https://github.com/rails/rails/commit/e8682c5bf051517b0b265e446aa1a7eccfd47bf7
Uploaded files assigned to a record are persisted to storage when the record
is saved instead of immediately.
In Rails 5.2, the following causes an uploaded file in `params[:avatar]` to
be stored:
```ruby
@user.avatar = params[:avatar]
```
In Rails 6, the uploaded file is stored when `@user` is successfully saved.
You can use awesome https://github.com/musaffa/file_validators gem
class Profile < ActiveRecord::Base
has_one_attached :avatar
validates :avatar, file_size: { less_than_or_equal_to: 100.kilobytes },
file_content_type: { allow: ['image/jpeg', 'image/png'] }
end
I'm using it with form object so I'm not 100% sure it is working directly with AR but it should...
Here is my solution to validate content types in Rails 5.2, that as you may know it has the pitfall that attachments are saved as soon as they are assigned to a model. It may also work for Rails 6. What I did is monkey-patch ActiveStorage::Attachment
to include validations:
config/initializers/active_storage_attachment_validations.rb
:
Rails.configuration.to_prepare do
ActiveStorage::Attachment.class_eval do
ALLOWED_CONTENT_TYPES = %w[image/png image/jpg image/jpeg].freeze
validates :content_type, content_type: { in: ALLOWED_CONTENT_TYPES, message: 'of attached files is not valid' }
end
end
app/validators/content_type_validator.rb
:
class ContentTypeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, _value)
return true if types.empty?
return true if content_type_valid?(record)
errors_options = { authorized_types: types.join(', ') }
errors_options[:message] = options[:message] if options[:message].present?
errors_options[:content_type] = record.blob&.content_type
record.errors.add(attribute, :content_type_invalid, errors_options)
end
private
def content_type_valid?(record)
record.blob&.content_type.in?(types)
end
def types
Array.wrap(options[:with]) + Array.wrap(options[:in])
end
end
Due to the implementation of the attach
method in Rails 5:
def attach(*attachables)
attachables.flatten.collect do |attachable|
if record.new_record?
attachments.build(record: record, blob: create_blob_from(attachable))
else
attachments.create!(record: record, blob: create_blob_from(attachable))
end
end
end
The create!
method raises an ActiveRecord::RecordInvalid
exception when validations fail, but it just needs to be rescued and that's all.
Came across this gem: https://github.com/igorkasyanchuk/active_storage_validations
class User < ApplicationRecord
has_one_attached :avatar
has_many_attached :photos
validates :name, presence: true
validates :avatar, attached: true, content_type: 'image/png',
dimension: { width: 200, height: 200 }
validates :photos, attached: true, content_type: ['image/png', 'image/jpg', 'image/jpeg'],
dimension: { width: { min: 800, max: 2400 },
height: { min: 600, max: 1800 }, message: 'is not given between dimension' }
end
Copy contents of the ActiveStorage's DirectUploadsController in the app/controllers/active_storage/direct_uploads_controller.rb
file and modify the create method. You can add authentication to this controller, add general validations on the file size or mime type, because create method of this controller creates the url for the file to be uploaded. So you can prevent any file upload by controlling size and mime type in this controller.
A simple validation could be:
# ...
def create
raise SomeError if blob_args[:byte_size] > 10240 # 10 megabytes
blob = ActiveStorage::Blob.create_before_direct_upload!(blob_args)
render json: direct_upload_json(blob)
end
# ...
I found a way to validate and delete attachments with callback before_save. This is a useful approach because if you validate file during the transaction (and you want to purge it), after adding error and it will rollback deleting the attachment.
before_save :check_logo_file, on: %i[create update]
def check_favicon_content_type
PartnerValidators::CustomPartnerFaviconValidator.new.validate(self)
end
module PartnerValidators
class CustomPartnerFaviconValidator < ActiveModel::Validator
ALLOWED_MIME_TYPES = %w(image/vnd.microsoft.icon image/x-icon image/png).freeze
private_constant :ALLOWED_MIME_TYPES
def validate(partner)
if partner.favicon.attached? && invalid_content_type?(partner)
partner.errors.add(:favicon, I18n.t("active_admin.errors.favicon"))
partner.favicon.purge
end
end
private
def invalid_content_type?(partner)
!partner.favicon.blob.content_type.in?(ALLOWED_MIME_TYPES)
end
end
end
来源:https://stackoverflow.com/questions/48158770/activestorage-file-attachment-validation