amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: us-east-1
bucket: <%= Rails.application.credentials.dig(:aws, :bucket) %>
class User < ApplicationRecord
has_one_attached :avatar
validates :avatar, content_type: ['image/png', 'image/jpg', 'image/jpeg'],
size: { less_than: 5.megabytes }
end
module Api
module V1
class AvatarsController < BaseController
before_action :authenticate_user!
def create
current_user.avatar.attach(params[:avatar])
if current_user.avatar.attached?
render json: {
url: url_for(current_user.avatar),
thumb_url: url_for(current_user.avatar.variant(resize_to_limit: [200, 200]))
}, status: :created
else
render json: { errors: current_user.errors }, status: :unprocessable_entity
end
end
def destroy
current_user.avatar.purge
render json: { message: 'Avatar deleted' }, status: :ok
end
end
end
end
ActiveStorage provides a unified interface for uploading files to cloud storage services like S3, GCS, or Azure Storage. I configure storage services in config/storage.yml and attach files to models using has_one_attached or has_many_attached macros. Files are stored with metadata in the database while blobs live in the configured storage service. Variants support on-the-fly image transformations using ImageProcessing gem. Direct uploads from browser to S3 bypass the Rails app entirely, improving performance and reducing server load. For APIs, I generate presigned URLs for clients to upload directly, then create the ActiveStorage attachment after successful upload. Proper security includes validating content types and file sizes before processing.