Question

I have a file field for people that want to upload a profile image:

<%= f.file_field :file %>

And it all works okay, however, I don't know how to validate it.

Here's my create action, and it's all working perfectly, but splitting the params hash like I'm doing is probably wrong:

def create
    new_user_params = user_params

    image_params = user_params[:profile_image_attributes]
    new_user_params.delete("profile_image_attributes")

    @user = User.new(new_user_params)


    respond_to do |format|
      if @user.save

        @user.create_thumbnail(image_params)

        sign_in @user

        format.html { redirect_to @user, notice: 'Welcome to ' + request.host_with_port + ', ' + @user.user_name + '!' }
        format.json { render action: 'show', status: :created, location: @user }
      else
        format.html { render action: 'new' }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
end

and here's the user#create_thumbnail method:

def create_thumbnail(data)

    upload =  data['file'].read

    img = Magick::Image.from_blob(upload).first

    img.resize!(75,75)      

    transaction do

        self.create_profile_image(path: 'test') 

        img.write 'test' + '.' + img.format.downcase

    end
end

Question:

Like it say, it all works perfectly, but I'd like to know how to do it properly, and, most importantly, how to stop people uploading huge files and files that don't have the format .gif, .png .jpg or .jpeg with appropriate validation error messages...

Was it helpful?

Solution

If you are open for adding a new gem then I would highly recommend using popular Paperclip gem which has built-in validations for file size, content type/ extension and presence.

Refer to Paperclip Github Documentation.

It would be as simple as saying

validates_attachment :image, :presence => true,
  :content_type => { :content_type => ["image/jpg", "image/jpeg", "image/gif", "image/png"] },
  :size => { :in => 0..10.kilobytes }

For a model with field named image(Paperclip attachment).

Where:

:presence Validates that a file was attached while form submission

:content_type Validates file extensions(mime-type) specified. It will also check the actual content of the uploaded file. Read my findings here.

:size Validates the uploaded file size against the given range.

OTHER TIPS

If you are uploading files, you might want to use Paperclip or Carrierwave. They both come stocked with features to easily implement the problems you are encountering.

Paperclip:

You can use validates in the model:

validates_attachment_presence
validates_attachment_content_type
validates_attachment_size

Older RailsCast featuring paperclip: http://railscasts.com/episodes/134-paperclip

Carrierwave:

Has you specify an uploader class and from there you can add validations for file type and size.

class MyUploader < CarrierWave::Uploader::Base
  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

Older RailsCast featuring Carrierwave: http://railscasts.com/episodes/253-carrierwave-file-uploads

Why not to use a gem for that as carrierwave? https://github.com/carrierwaveuploader/carrierwave

If you want to use your code why not to refactor a bit, for example

I would do something like this, using some design patterns.

def create_thumbnail
  upload_file(read_file(data))
  resize_img
  execute_transaction
end

def read_file(data)
 data['file'].read
end

def upload_file(read_file)
  Magick::Image.from_blob(read_file).first
end

def resize_img
 upload_file.resize!(75,75)
end

def execute_transaction
   transaction do

      self.create_profile_image(path: 'test') 

      img.write 'test' + '.' + img.format.downcase

   end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top