File uploading is a common action for websites. And for Rails applications, CarrierWave gem provides a very simple and flexible way to achieve that.

In realistic situation, you may only allow user to be able to upload files within a limited size or with certain type. For example, if you only allow image file to be uploaded, you probably want to restrict the file extensions to .jpg .jpeg .gif or .png. You probably also need to limit the file size to e.g. 5 MB maximum.

In this post, I will show you how to do validations for file size and file extensions in a Rails application. The validations should be implemented on both front end (client side) and back end (server side).

In the following example, I assume you’ve created an User model, and included a string field :avatar to mount the uploader of CarrierWave, following this guide.

1. Client Side Validation

The front end side (client side) is implemented using jQuery.

First, we need to create a file field inside of your form, using Rails’ file field helper:

<%= f.file_field :avatar, onchange: "validateFiles(this);",
    data: { max_file_size: 5.megabytes }
%>

So that, the onChange event will be triggered when the file is selected, and a method called validateFiles will be called. And note that I create a data attribute max_file_size to store the maximum allowed file size information. You can change this value to suit your needs.

In the next step, we need to implement the validateFiles method. Put the following javascript code inside of your .js file:

function validateFiles(inputFile) {
  var maxExceededMessage = "This file exceeds the maximum allowed file size (5 MB)";
  var extErrorMessage = "Only image file with extension: .jpg, .jpeg, .gif or .png is allowed";
  var allowedExtension = ["jpg", "jpeg", "gif", "png"];

  var extName;
  var maxFileSize = $(inputFile).data('max-file-size');
  var sizeExceeded = false;
  var extError = false;

  $.each(inputFile.files, function() {
    if (this.size && maxFileSize && this.size > parseInt(maxFileSize)) {sizeExceeded=true;};
    extName = this.name.split('.').pop();
    if ($.inArray(extName, allowedExtension) == -1) {extError=true;};
  });
  if (sizeExceeded) {
    window.alert(maxExceededMessage);
    $(inputFile).val('');
  };

  if (extError) {
    window.alert(extErrorMessage);
    $(inputFile).val('');
  };
}

Basically, what this piece of code does is to check if the input file is over the limit and if the input file type is the one included in the array allowedExtension. If validations fail, an alert window will pop up, and the input field will get cleaned.

You can customize the error messages by modifying the values of maxExceededMessage and extErrorMessage, you can also change the allowed file extensions by updating the allowedExtension array.

That’s all for the client side!

2. Server Side Validation

Only client side validation is not enough, people can easily bypass it. We need to add the same validations to the server side. For Rails applications, the common way is set it up in the model.

For file extension check, you need to add the #extension_white_list method inside of your uploader class:

class AvatarUploader < CarrierWave::Uploader::Base

  # ... ...

  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

Next, add a custom validator for file size validation. Inside of your User model, add the following code:

class User < ActiveRecord::Base
  # ... ...
  validate :avatar_size_validation

  # ... ...
  private

  def avatar_size_validation
    errors[:avatar] << "should be less than 5MB" if avatar.size > 5.megabytes
  end
end

And that’s it! You can apply this approach to any model with any file type. And the client side validation is not tied to Rails application at all, you can use the same code to any type of application.