Uploading files into your Rails application has never been easier, and with the a wealth of useful gems around to help there's no better time to be working with files.

In this tutorial, I'm going to run through uploading an image (although the same technique can be applied to any type of file) to a model, displaying it in the web browser, validating the uploaded file, and allowing it to be subsequently deleted. We'll assume that we're building a blog and we want to add attachments for a cover photo for each post.

Getting started

To begin, you'll need to get the attach gem installed into your application. Go ahead and add it your Gemfile. This is a gem that I've written, so I know it inside-out.

gem 'attach', '~> 1.0.1'
$ bundle install
$ rails restart

As we're storing our files in the database, we'll need to create some migrations for the tables that will actually store the file's meta data and binary content. Attach comes with a generator that will automatically add the necessary migrations to your application – run that, then migrate your database as normal.

$ rake attach:install:migrations
$ rake db:migrate

Configuring your model

There's no need to change the database schema for any of the tables that you want to attach files to. All the relation information is stored in the attachments table that we've just created.

You do, however, need to list the types of file that you wish to add to your model. Let's go ahead and add the cover_photo attachment to our Post model (in app/models/post.rb).

class Post < ActiveRecord::Base
  attachment :cover_photo
end

Uploading from a form

Next, we need to actually allow files to be uploaded from our application by adding the appropriate field to our post form. We'll begin by adding a file_field to our post's form.

<% form_for @post do |f| %>
  <!--- Your existing form fields -->
  <%= f.label :cover_photo %>
  <%= f.file_field :cover_photo %>
  <!--- A submit button -->
<% end %>

Now we need to ensure that we have allowed the cover_photo parameter to be permitted in our controller. Open up your controller and make sure that your params.require(:post).permit(...) line includes the cover_photo attribute. You might have something like this for your new and edit actions.

params.require(:post).permit(:title, :content, :cover_photo)

Accessing your uploaded files

The library provides various methods of accessing the files that are uploaded, the easiest is the built-in middleware that will serve your attachments. We'll make use of this now to display the cover photo on our blog post. Open up your show action's HTML and insert an image tag.

<% if @post.cover_photo %>
  <%= image_tag @post.cover_photo.url %>
<% end %>

The url method on an attachment will return a URL in your application that our middleware will intercept and render your chosen attachment. By default it will look something like /attachment/736a33aa-e1b2-453e-808c-5a668e6e2fa3/photo.jpg.

If you were uploading something that wasn't image, you may prefer to link to this file, rather than rendering it in the browser like this.

<%= link_to "Download product datasheet", @product.datasheet.url %>

Give it a test

Now you're ready to give this a go and see how it's working for you. Open up your browser, upload a cover photo your post and then try to view it. You should see the photo is uploaded and being displayed in your browser.

Adding some basic validation

At the moment, your application will accept any file that a user wishes to upload. We'll add a little bit of validation to ensure that the file uploaded looks like an image. This validation will only verify the content type provided by the user, so is only really designed to prevent accidental uploads of incorrect file types rather than for any security purposes.

Open up your Post model and add the following to your attachment.

class Post < ActiveRecord::Base
  attachment :cover_photo do
    validator do |attachment, errors|
      unless attachment.file_type =~ /\Aimage\/(jpeg|png|gif)/
        errors << "must have an image content type"
      end
    end
  end
end

When you now try to upload an image with the incorrect content type, an error will be added to your post model on save.

Deleting an attachment

There are two ways you can allow an attachment to be deleted from a model. The most simple is to simply call destroy on the attachment object.

def delete_cover_photo
  post = Post.find(params[:id]
  post.cover_photo.destroy
end

Alternatively, you can use a checkbox on your form to submit a request to delete an attachment when the model is saved. To implement this, we need to add a checkbox to our form as appropriate:

<% if f.object.cover_photo %>
  <%= f.check_box :cover_photo_delete %>
  <%= f.label :cover_photo_delete, "Delete cover photo?" %>
<% end %>

You'll also need to add cover_photo_delete to the list of permitted parameters in your controller.

params.require(:post).permit(:title, :content, :cover_photo_delete)

Now, give this a go. Open up your form, check the checkbox, and submit the form. You should find that the cover photo is removed when your post is successfully saved.

Next steps

Now, we've uploaded a file, and in some circumstances that'll be all that's required, however, there are other things that you should be looking at next. If you're working with images, like we are in our blog, you may want to automatically create some thumbnails of the images you upload. I'll cover this my next post, where we will discuss adding image resizing, and improving validation for ensuring an uploaded file is an actually image.

Tell us how you feel about this post?