Image Processing with Active Storage
Active Storage can transform images on-the-fly using variants. This is perfect for creating thumbnails, resizing images, and applying filters.
Setup
Add image processing gem:
ruby
1# Gemfile
2gem 'image_processing', '~> 1.2'Install ImageMagick or libvips:
bash
1# macOS
2brew install vips
3
4# Ubuntu
5sudo apt install libvipsCreating Variants
Basic Resize
erb
1<!-- Resize to fit within dimensions -->
2<%= image_tag @user.avatar.variant(resize_to_limit: [100, 100]) %>
3
4<!-- Resize to exact dimensions (crops if needed) -->
5<%= image_tag @user.avatar.variant(resize_to_fill: [100, 100]) %>
6
7<!-- Resize to fill, with gravity -->
8<%= image_tag @user.avatar.variant(resize_to_fill: [100, 100, { gravity: "Center" }]) %>
9
10<!-- Resize width only (height auto) -->
11<%= image_tag @user.avatar.variant(resize: "300x") %>
12
13<!-- Resize height only (width auto) -->
14<%= image_tag @user.avatar.variant(resize: "x200") %>Common Variant Options
ruby
1# Thumbnail (resize and crop)
2avatar.variant(resize_to_fill: [150, 150])
3
4# Limit size (maintain aspect ratio)
5avatar.variant(resize_to_limit: [800, 600])
6
7# Convert format
8avatar.variant(format: :webp)
9
10# Rotate
11avatar.variant(rotate: 90)
12
13# Quality (for JPEG)
14avatar.variant(saver: { quality: 80 })
15
16# Multiple transformations
17avatar.variant(
18 resize_to_fill: [200, 200],
19 format: :webp,
20 saver: { quality: 80 }
21)Named Variants
Define variants in your model for reuse:
ruby
1class User < ApplicationRecord
2 has_one_attached :avatar do |attachable|
3 attachable.variant :thumb, resize_to_fill: [100, 100]
4 attachable.variant :medium, resize_to_limit: [300, 300]
5 attachable.variant :large, resize_to_limit: [800, 800]
6 end
7enderb
1<%= image_tag @user.avatar.variant(:thumb) %>
2<%= image_tag @user.avatar.variant(:medium) %>
3<%= image_tag @user.avatar.variant(:large) %>Preprocessing Variants
Generate variants immediately on upload:
ruby
1class User < ApplicationRecord
2 has_one_attached :avatar do |attachable|
3 attachable.variant :thumb, resize_to_fill: [100, 100], preprocessed: true
4 end
5endOr use a job:
ruby
1class ProcessImageJob < ApplicationJob
2 def perform(user)
3 user.avatar.variant(:thumb).processed
4 user.avatar.variant(:medium).processed
5 end
6end
7
8# After upload
9class User < ApplicationRecord
10 after_commit :process_avatar_variants, on: [:create, :update]
11
12 private
13
14 def process_avatar_variants
15 ProcessImageJob.perform_later(self) if avatar.attached?
16 end
17endChecking Variant Status
ruby
1# Check if variant is processed
2user.avatar.variant(:thumb).processed?
3
4# Process synchronously
5variant = user.avatar.variant(:thumb).processed
6
7# Get variant URL
8url_for(user.avatar.variant(:thumb))Representation (for non-images)
For PDFs and videos, use representations:
ruby
1class Document < ApplicationRecord
2 has_one_attached :file
3enderb
1<% if @document.file.representable? %>
2 <%= image_tag @document.file.representation(resize_to_limit: [100, 100]) %>
3<% end %>Preview (for videos)
ruby
1class Video < ApplicationRecord
2 has_one_attached :file
3enderb
1<% if @video.file.previewable? %>
2 <%= image_tag @video.file.preview(resize_to_limit: [300, 300]) %>
3<% end %>Validations
Validate file types and sizes:
ruby
1class User < ApplicationRecord
2 has_one_attached :avatar
3
4 validates :avatar, content_type: ['image/png', 'image/jpeg', 'image/gif'],
5 size: { less_than: 5.megabytes }
6end
7
8# Or with active_storage_validations gem
9# Gemfile: gem 'active_storage_validations'
10
11class User < ApplicationRecord
12 has_one_attached :avatar
13
14 validates :avatar, attached: true,
15 content_type: [:png, :jpg, :jpeg],
16 size: { less_than: 5.megabytes },
17 dimension: { width: { min: 100, max: 2000 },
18 height: { min: 100, max: 2000 } }
19endCustom Transformations
ruby
1# Using ImageMagick/libvips options directly
2avatar.variant(
3 resize_to_fill: [200, 200],
4 monochrome: true,
5 rotate: 45
6)
7
8# Complex transformation
9avatar.variant(
10 resize_to_fill: [400, 400],
11 format: :jpg,
12 saver: {
13 quality: 85,
14 strip: true # Remove metadata
15 }
16)Responsive Images
erb
1<picture>
2 <source
3 srcset="<%= url_for(@article.image.variant(resize_to_limit: [1200, 1200])) %>"
4 media="(min-width: 1200px)">
5 <source
6 srcset="<%= url_for(@article.image.variant(resize_to_limit: [800, 800])) %>"
7 media="(min-width: 800px)">
8 <source
9 srcset="<%= url_for(@article.image.variant(resize_to_limit: [400, 400])) %>"
10 media="(min-width: 400px)">
11 <%= image_tag @article.image.variant(resize_to_limit: [400, 400]),
12 alt: @article.title,
13 loading: "lazy" %>
14</picture>Using with CDN
ruby
1# config/environments/production.rb
2config.active_storage.service = :amazon
3
4# Use CDN for serving
5config.active_storage.resolve_model_to_route = :rails_storage_proxy
6
7# Or custom CDN host
8Rails.application.routes.default_url_options[:host] = 'cdn.example.com'Image variants make it easy to serve optimized images for any use case!
