I recently came across a situation where I needed to be able to upload a file to a Rails server with Active Admin. I did a quick search on Google and found this post by Job, a fellow Atom.
Our use cases were a little bit different, though. He was storing the file contents directly in the database, whereas I needed to be able to uplaod a firmware image file to the server’s filesystem, parse the file name, and perform some validations on the file. I decided to use the Paperclip gem to manage the file processing and storage. Using Job’s advice on Active Admin file uploads, I expanded his example to incorporate Paperclip.
What is Paperclip?
Paperclip, as its name suggests, is a file attachment library for ActiveRecord. It is designed to treat files much like other attributes, and it provides a whole slew of built-in extensions (e.g. validations, pre/post processing callback hooks, security enhancements, etc.). The setup is very simple, and getting the basics up and running takes only a few minutes. For the sake of this example, we will set up the ActiveRecord model, create a migration, and get rolling with the Active Admin file upload.
Install the Paperclip Gem
Installing the gem is easy. Just add
gem 'paperclip' to your Gemfile and run
bundle install Create a Migration
If you have an existing model where you want to add Paperclip support, then you simply need to create a migration. In this case, I already had a table and a corresponding model for my firmware image, so I just needed to add a migration to add the columns that Paperclip requires. The easiest way to do this is with the Rails migration generator:
rails generate paperclip firmware image This will automatically create a migration file that looks like this:
class AddImageColumnsToFirmware < ActiveRecord::Migration def up add_attachment :firmware, :image end def down remove_attachment :firmware, :image end end The add_attachment helper will automatically create the following columns in your table:
- image_file_name
- image_content_type
- image_file_size
- image_updated_at
Technically, only the *_file_name column is required for Paperclip to operate, so you could throw away the others if you don’t need them.
Paperclip Your Models
The true beauty of Paperclip is how well it integrates with ActiveModel. To add Paperclip support to your model, start by adding the following line to your model class:
class Firmware < ActiveRecord::Base has_attached_file :image # more stuff to come end With that one line, Paperclip is now integrated with your Rails model, and it will automatically handle your file “attachments” just like any other Rails attribute! There are, of course, plenty of other options that you can add to the has_attached_file attribute (e.g. specifying a file path, style type, etc.), but I won’t go into that right now. For our purposes, the defaults should be just fine.
Validations!
Paperclip also makes it really easy to perform powerful validations on the file. If any validation fails, the file will not be saved to disk and the associon will be rolled back, just like any other ActiveRecord validation. There are built-in helpers to validate attachment presence, size, content type, and file name.
In our case, we really just need to validate the file name to ensure that it had the proper format and also that it was a unique file name. The following validation did the trick:
validates_attachment_file_name :image, :matches => [/_\d+_\d+_\d+\.bin$/] validates_uniqueness_of :image_file_name # this is a standard ActiveRecord validator Additional Processing
I also wanted to be able to grab the firmware version out of the file name. The best way to do this is with a before_post_process callback in the model, like this:
before_post_process :parse_file_name def parse_file_name version_match = /_(?\d+)_(?\d+)_(?\d+)\.bin$/.match(image_file_name) if version_match.present? and version_match[:major] and version_match[:minor] and version_match[:patch] self.major_version = version_match[:major] self.minor_version = version_match[:minor] self.patch_version = version_match[:patch] end end Before the file is saved, but after the filename is validated (so we can be sure it has the proper formatting), we extract the major, minor, and patch numbers from the filename and save them in our database.
Configure Active Admin for File Upload
Now that Paperclip is all set up and wired into our Rails model, we need to actually set up the file upload piece in Active Admin. I won’t go into much detail, since I relied on Job’s post as a reference. Basically, all we need to do is:
- Define the Index page contents
Enumerate which columns will be displayed. If any special decoration is required, such as customized column title, sort properties, or row formatting, they can be specified easily. For example, I wanted a link to download the firmware image, so I added a
link_toin the “Image” column. Note that the file path is stored in theimage.urlattribute. - Specify which parameters may be changed
Use the
permit_paramsmethod to whitelist any attributes. - Create the upload form
Use the
f.input :image, as: :filesyntax to automatically create a file upload field in Active Admin.
The code snippet below is the Active Admin page, which allows the user to create, view, edit, and delete a firmware image.
ActiveAdmin.register Firmware do permit_params :image index do selectable_column id_column column 'Image', sortable: :image_file_name do |firmware| link_to firmware.image_file_name, firmware.image.url end column :image_file_size, sortable: :image_file_size do |firmware| "#{firmware.image_file_size / 1024} KB" end column "Version" do |firmware| "#{firmware.major_version}.#{firmware.minor_version}.#{firmware.patch_version}" end column :created_at actions end form do |f| f.inputs "Upload" do f.input :image, required: true, as: :file end f.actions end end And that’s it! We now have a fully functional file-upload implementation using Active Admin backed by Paperclip. It’s really quite simple, and it only took a few minutes to get it set up and running.

Thanks! Great, succinct writeup and precisely what I needed. Will give this a go tonight – appreciate the pointers!
Hey!
How would I make it so that I have the choice to either upload a file OR give a link to an exisiting file already online so that I don’t have to take up my space on my personal server.
Hi Keith,
Thanks for the comment. In Paperclip, you can set the attachment as a URL with the following syntax:
image = URI.parse(“http://example.com/some_image.bin”).
So you could have the option to enter a URL instead of uploading a file, and set the “image” attribute with the URI.parse’d URL. I’m actually not sure if this will instantly go download the file or if it will lazily fetch the file when needed, so you might have to test that. You can also store the downloaded file in a temp folder on your server so it doesn’t get persisted if that’s what you want. I hope this helps!
and if I want to load several images How can you do?