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.
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
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
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 %>
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
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
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.
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.