Ruby on Rails - File Uploading
Ruby on Rails - Modern File Uploading with Active Storage
Section titled “Ruby on Rails - Modern File Uploading with Active Storage”Many web applications require users to upload files (e.g., profile pictures, documents). Rails provides a built-in solution called Active Storage for handling file uploads and attachments to Active Record models. Active Storage supports storing files on local disk for development/testing and various cloud storage services (like Amazon S3, Google Cloud Storage, Microsoft Azure Storage) for production.
Let’s build a simple Rails application to upload resumes.
Step 1: Create a New Rails Application
Section titled “Step 1: Create a New Rails Application”$ rails new resume_uploader_app$ cd resume_uploader_appStep 2: Install Active Storage
Section titled “Step 2: Install Active Storage”Active Storage requires database tables to store metadata about the uploaded files. Run the installer:
$ bin/rails active_storage:installThis command generates a migration file. Now, run the migration:
$ bin/rails db:migrateThis creates active_storage_blobs and active_storage_attachments tables.
Step 3: Configure Storage Service
Section titled “Step 3: Configure Storage Service”Open config/storage.yml. By default, it’s configured to use local disk storage for development and testing:
test: service: Disk root: <%= Rails.root.join("tmp/storage") %>
local: service: Disk root: <%= Rails.root.join("storage") %>In config/environments/development.rb (and production.rb), specify which service to use:
# In config/environments/development.rbRails.application.configure do # ... other configurations ... config.active_storage.service = :localendFor production, you would configure a cloud service like Amazon S3 here.
Step 4: Create the Model and Controller
Section titled “Step 4: Create the Model and Controller”Let’s generate a Resume model and a ResumesController:
$ bin/rails generate model Resume name:string$ bin/rails db:migrate$ bin/rails generate controller Resumes index new create destroy --no-helper --no-assets --no-test-frameworkModify the Resume model (app/models/resume.rb) to declare an attachment:
class Resume < ApplicationRecord has_one_attached :attachment
validates :name, presence: true validates :attachment, presence: true # Ensure a file is uploaded # For more specific validations (content type, size): # validates :attachment, content_type: ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], # size: { less_than: 5.megabytes , message: 'file is too large (max 5MB)' }endhas_one_attached :attachment tells Active Storage that a Resume can have one file attached, referred to as attachment.
Step 5: Define Routes
Section titled “Step 5: Define Routes”Update config/routes.rb:
Rails.application.routes.draw do resources :resumes, only: [:index, :new, :create, :destroy] root "resumes#index"endStep 6: Implement Controller Actions
Section titled “Step 6: Implement Controller Actions”Edit app/controllers/resumes_controller.rb:
class ResumesController < ApplicationController def index @resumes = Resume.all end
def new @resume = Resume.new end
def create @resume = Resume.new(resume_params) if @resume.save redirect_to resumes_path, notice: "The resume '#{@resume.name}' has been uploaded." else render :new, status: :unprocessable_entity end end
def destroy @resume = Resume.find(params[:id]) @resume.destroy redirect_to resumes_path, notice: "The resume '#{@resume.name}' has been deleted.", status: :see_other end
private
def resume_params params.require(:resume).permit(:name, :attachment) endendNote that :attachment is permitted in resume_params.
Step 7: Create Views
Section titled “Step 7: Create Views”Create app/views/resumes/index.html.erb:
<h1>Resumes</h1>
<% if flash[:notice].present? %> <p style="color: green;"><%= flash[:notice] %></p><% end %>
<%= link_to "Upload New Resume", new_resume_path %>
<hr>
<% if @resumes.any? %> <table> <thead> <tr> <th>Name</th> <th>Attachment</th> <th>Actions</th> </tr> </thead> <tbody> <% @resumes.each do |resume| %> <tr> <td><%= resume.name %></td> <td> <% if resume.attachment.attached? %> <%= link_to resume.attachment.filename, url_for(resume.attachment), target: "_blank" %> (<%= number_to_human_size(resume.attachment.blob.byte_size) %>) <% else %> No attachment <% end %> </td> <td><%= button_to "Delete", resume_path(resume), method: :delete, data: { turbo_confirm: "Are you sure?" } %></td> </tr> <% end %> </tbody> </table><% else %> <p>No resumes uploaded yet.</p><% end %>Create app/views/resumes/new.html.erb:
<h1>Upload New Resume</h1>
<%= form_with(model: @resume, local: true) do |form| %> <% if @resume.errors.any? %> <div style="color: red;"> <h2><%= pluralize(@resume.errors.count, "error") %> prohibited this resume from being saved:</h2> <ul> <% @resume.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>
<div> <%= form.label :name %><br> <%= form.text_field :name %> </div>
<div> <%= form.label :attachment %><br> <%= form.file_field :attachment %> </div>
<div> <%= form.submit %> </div><% end %>
<%= link_to "Back to resumes", resumes_path %>The key part for file uploads is form.file_field :attachment. form_with(model: @resume, local: true) is used; for Active Storage with direct uploads (to cloud), local: true might be omitted depending on setup.
Step 8: Test the Application
Section titled “Step 8: Test the Application”- Start the server:
$ bin/rails server - Go to
http://localhost:3000. - Click “Upload New Resume”. The page will show a form with fields for ‘Name’ and ‘Attachment’ (a file input).
- Enter a name, choose a file, and click “Create Resume” (or whatever the submit button is named).
- If successful, you’ll be redirected to the index page, and your uploaded resume will be listed. The filename will be a link to view/download the file. You’ll also see its size.
- If there are validation errors (e.g., no name provided, or file type/size restrictions if you added them in the model), the ‘new’ page will re-render with error messages. For example, if you left the name blank, you’d see “Name can’t be blank”. If you uploaded a very large file (if size validation is active), you might see “Attachment file is too large”.
Active Storage provides a robust and integrated way to handle file uploads in Rails, simplifying a common but complex task.