Проверка вложений в ActiveStorage File Attachment
Есть ли способ проверить вложения с ActiveStorage? Например, если я хочу проверить тип содержимого или размер файла?
Что-то вроде подхода "Скрепка" было бы здорово!
validates_attachment_content_type :logo, content_type: /\Aimage\/.*\Z/
validates_attachment_size :logo, less_than: 1.megabytes
Ответы
Ответ 1
Ну, это некрасиво, но это может быть необходимо, пока они не испекут некоторые валидации:
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
Ответ 2
ActiveStorage не поддерживает проверки прямо сейчас. Согласно https://github.com/rails/rails/issues/31656.
Обновить:
Rails 6 будет поддерживать валидации ActiveStorage.
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.
Ответ 3
Вы можете использовать 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
Я использую его с объектом формы, поэтому я не уверен на 100%, что он работает напрямую с AR, но он должен...
Ответ 4
Наткнулся на этот драгоценный камень: 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
Ответ 5
Скопируйте содержимое ActiveStorage DirectUploadsController в файл app/controllers/active_storage/direct_uploads_controller.rb
и измените метод создания. Вы можете добавить аутентификацию к этому контроллеру, добавить общие проверки на размер файла или тип mime, потому что метод create этого контроллера создает URL-адрес для файла, который будет загружен. Таким образом, вы можете предотвратить загрузку любого файла, контролируя размер и тип mime в этом контроллере.
Простая проверка может заключаться в следующем:
# ...
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
# ...
Ответ 6
Я нашел способ проверить и удалить вложения с обратным вызовом before_save. Это полезный подход, потому что если вы проверяете файл во время транзакции (и вы хотите очистить его), после добавления ошибки и откат удалит вложение.
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
Ответ 7
Вот мое решение для проверки типов контента в Rails 5.2, которое, как вы, возможно, знаете, имеет тот недостаток, что вложения сохраняются, как только они назначаются модели. Это также может работать для Rails 6. Что я сделал, так это monkey-patch ActiveStorage::Attachment
для включения проверок:
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
Благодаря реализации метода attach
в 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
create!
Метод вызывает исключение ActiveRecord::RecordInvalid
когда проверки не ActiveRecord::RecordInvalid
, но его просто нужно спасти и все.