File Validations
File validations are a must for any web application that handles file uploads. Especially, if your web app has public access or is made for public use or will be used by multiple users. Say for instance if you have a photosharing web app; where users can upload photos. If you didn’t validate the uploaded files with file size, format and other parameters; users would upload photos with huge file sizes and even worse if they uploaded files of unsupported format, sometimes by mistakes or even intentionally. This would let your web app to potentially crash and will not display the uploaded files(photos in this case), properly.
The file_validators gem
There’s a really quick and easy solution to add file validations to your app; with this gem called ‘file_validators’. This gem is very easy to use yet it gives complete file validation solution. This gem adds file size and content type validations to ActiveModel. Any module that uses ActiveModel; can use these file validators. This gem supports rails6 and also has support for a wide range of uploading solutions like active storage, paperclip, carrierwave, dragonfly, etc.
Github Link: https://github.com/musaffa/file_validators
Installing This Gem
To get started, add file_validators to your gemfile and run bundler.
#File: Gemfile
gem 'file_validators'
bundle install
Using This Gem
Lets assume that you already have an app with basic file uploads already setup, you can use active storage or any other file uploader gem. Now for instance, you have an avatar attachment for the profile pics/avatars for the users. For example, here i’m using using active storage, and i have an :avatar_data attachment for the avatar image.
The Model
Since file_validators does validations through the active model, i would add those validations in my model like this. I also added custom error messages, to make it look a lot better.
#File: app/models/user.rb
class User < ApplicationRecord
has_one_attached :avatar_data
validates :avatar_data, presence: true
validates :avatar_data, file_size: { less_than_or_equal_to: 4.megabytes, message: "Please Check File Size" },
file_content_type: { allow: ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'],
message: "Please Check File Format"}
end
Basic File Validations
You can do basic file validations like these
To Validate File Sizes
Validate between or, in range
validates :avatar_data, file_size: { in: 1.megabyte..4.megabytes }
Validate less than
validates :avatar_data, file_size: { less_than: 100.megabytes }
Validate less than or equal to
validates :avatar_data, file_size: { less_than_or_equal_to: 500.kilobytes }
Validate greater than
validates :avatar, file_size: { greater_than: 500.kilobytes }
Validate greater than or equal to
validates :avatar, file_size: { greater_than_or_equal_to: 5.megabytes }
To Validate File Types
Validate single file type
validates :avatar_data, file_content_type: { allow: 'image/jpeg' }
Validate multiple/array of file types
validates :avatar_data, file_content_type: { allow: ['image/jpeg', 'image/png', 'image/gif'] }
Validate with regex
validates :avatar, file_content_type: { allow: /^image\/.*/ }
Validate with multiple/array of regex
validates :attachment, file_content_type: { allow: [/^image\/.*/, /^video\/.*/] }
There are also other types of validations that you can perform, please check out file_validators docs to get the complete guide.
Large File Sizes
When using this gem, file validations are done through the model. This means that the file will be first uploaded to the app server and then processed on the app server. Handling huge file size uploads are not recommended with this approach, but it works great will small files like avatars images, photos, etc. If you want to do file validation for huge files without putting a lot of load on the app server; i recommend you to use direct upload javascript libraries like DropzoneJS.
Direct Uploads Validation
File_validators gem does not work with direct uploads, if you want to validate files for direct uploads, it can be done with many javascript libraries, for example; i have used dropzone.js for direct uploads, and file validation works seamlessly with dropzone uploader.
If you are using dropzone, you can do it like this:
#File: app/views/users/_form.html.erb
<div class="dropzone dropzone-default dz-clickable" data-controller="dropzone" data-dropzone-max-file-size="4" data-dropzone-accepted-files="image/png, image/jpg, image/gif">
<%= form.file_field :avatar_data, direct_upload: true, data: { target: 'dropzone.input' } %>
<div class="dropzone-msg dz-message needsclick">
<h3 class="dropzone-msg-title">Select File To Upload</h3>
<span class="dropzone-msg-desc">Max Files: 1, Max File Size: 4MiB, File Formats: .png, .jpg, .gif</span>
</div>
</div>
Final Thoughts
File validation can also be done from scratch without using any gem. Also, there are many other gems that can do this, but i just find file_validators a lot easier to work with. Please don’t forget to check out the documentation and have a nice day.
File Validators Docs: https://www.rubydoc.info/gems/file_validators/2.3.0