DEV Community

Cover image for Active Storage Ruby on Rails
Steven Gutierrez
Steven Gutierrez

Posted on

Active Storage Ruby on Rails

In today's guide, we'll explore how to seamlessly integrate Active Storage, a powerful feature of Ruby on Rails, into your web application. Active Storage allows you to manage file uploads, making it an essential tool for any project that requires handling images, documents, or any other form of file attachments. We will create a simple User model and configure image uploading for user avatars. We'll also use Active Storage Validations and Mini_Magick for image processing.

For this guide an assumption is made that you already know how to setup and create a Ruby on Rails application that uses React JS for the frontend.


Prerequisites

Before we begin, ensure you have the following:

  • Ruby on Rails installed and setup for your project
  • Node.js and Yarn installed (for React)
  • Basic knowledge of Ruby on Rails and React

Step 1: Setting Up a New Rails Application

Let's start by creating a new Rails application. Open your terminal and run:

rails new ActiveStorageExample --database=postgresql
cd ActiveStorageExample
Enter fullscreen mode Exit fullscreen mode

This command will create a new Rails application named "ActiveStorageExample" using PostgreSQL as the database.


Step 2: Configuring Active Storage

Now, let's configure Active Storage for our application. Install Active Storage in the terminal with:

rails active_storage install 
rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Add the following line to your config/application.rb file:

config.active_storage.variant_processor = :mini_magick
Enter fullscreen mode Exit fullscreen mode

This line configures Active Storage to use MiniMagick for image processing. If you don't need to process the images being uploaded you can skip this part


Step 3: Creating the User Model

Next, let's generate a User model and all necessary files with a user name, first/last name and an avatar image. Run the following command:

rails generate scaffold User username first_name last_name
rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Now, let's add Active Storage to our User model. In app/models/user.rb, make sure it looks like this:

class User < ApplicationRecord
 has_one_attached :avatar
end
Enter fullscreen mode Exit fullscreen mode

This associates the User model with Active Storage and enables us to attach avatar images.


Step 4: Updating the User Controller

In your app/controllers/users_controller.rb, add the avatar attribute to the list of permitted parameters:

def user_params
 params.require(:user).permit(:name, :first_name, :last_name, :avatar)
end
Enter fullscreen mode Exit fullscreen mode

Step 5: Setting Up React as the Frontend

In this example, we'll use React as our frontend library. You can set up your React frontend as needed. Make sure you've installed the required dependencies and also set up your React components as you see fit. For reference I will use this form example below to handle the user data upload:

import React, { useState, useRef } from 'react';

const UserForm = () => {
  const [userData, setUserData] = useState({
    username: '',
    firstName: '',
    lastName: '',
  });
  const imageFile = useRef(null);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setUserData({ ...userData, [name]: value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    const formData = new FormData();
    formData.append('user[username]', userData.username);
    formData.append('user[first_name]', userData.firstName);
    formData.append('user[last_name]', userData.lastName);
    formData.append('user[avatar]', imageFile.current.files[0]);

    try {
      const response = await fetch('/users', {
        method: 'POST',
        body: formData,
        // by using FormData() headers are not necessary
      });

      if (response.ok) {
        response.json().then((userData) => {
          console.log(userData)
        });
      } else {
        // Handle errors (e.g., show an error message)
        console.error('Error updating data.');
      }
    } catch (error) {
      console.error('An error occurred while submitting data:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit} method="post" >
      <div>
        <label htmlFor="username">Username:</label>
        <input
          type="text"
          id="username"
          name="username"
          value={userData.username}
          onChange={handleChange}
        />
      </div>
      <div>
        <label htmlFor="firstName">First Name:</label>
        <input
          type="text"
          id="firstName"
          name="firstName"
          value={userData.firstName}
          onChange={handleChange}
        />
      </div>
      <div>
        <label htmlFor="lastName">Last Name:</label>
        <input
          type="text"
          id="lastName"
          name="lastName"
          value={userData.lastName}
          onChange={handleChange}
        />
      </div>
      <div>
        // Insert Image file functionality here...
      </div>
      <div>
        <button type="submit">Submit</button>
      </div>
    </form>
  );
};

export default UserForm;
}

Enter fullscreen mode Exit fullscreen mode

Step 6: Adding Image Upload Functionality

Now, let's add image upload functionality to your React frontend. Create an input that allows users to select and upload their avatars.

Here's a simplified example:

    <div>
        <label htmlFor="avatar">Avatar:</label>
        <input
          type="file"
          id="avatar"
          name="avatar"
          ref={imageFile}
        />
    </div>
Enter fullscreen mode Exit fullscreen mode

Step 7: Adding Active Storage Validations

Active Storage Validations is a gem that provides easy-to-use validation rules for your attachments. Add it to your Gemfile:

gem 'active_storage_validations'
Enter fullscreen mode Exit fullscreen mode

Make sure to install the gem:

bundle install 
Enter fullscreen mode Exit fullscreen mode

In your User model, add the following validations:

class User < ApplicationRecord

  has_one_attached :avatar

  validates :avatar, content_type: ['image/png', 'image/jpg', 'image/jpeg']

end
Enter fullscreen mode Exit fullscreen mode

Step 8: Processing Images with MiniMagick

If you need to manipulate the uploaded images, you can use MiniMagick. It's already configured in your Rails application.

In your controller or a service, you can use MiniMagick to process the images. Here's an example of resizing the avatar:

user.avatar.variant(resize: '100x100').processed
Enter fullscreen mode Exit fullscreen mode

By resizing the image down to 100x100-pixel square, we will allow for faster load times for our app.


Step 9: Ensuring that the image data is being sent to the frontend

Much of the configuration and validations are handled at this point, however we need to send the image back to the front end to be rendered client-side.

In your User model add a Rails helper and custom method:

class User < ApplicationRecord
  include Rails.application.routes.url_helpers

  has_one_attached :avatar

  validates :avatar, content_type: ['image/png', 'image/jpg', 'image/jpeg']

  def image_url 
    url_for(self.image)
  end
end
Enter fullscreen mode Exit fullscreen mode

In your Serializer include this method to format the User image, make sure to pass the method name as an attribute:

def image_format 
    return unless object.image.attached?
    object.image.blob.attributes
        .slice('filename','byte_size')
        .merge(url: object.image_url)
        .tap { |attrs| attrs['name'] = attrs.delete('filename')}
end
Enter fullscreen mode Exit fullscreen mode

In your development.rb file located in config/environment folder include the default url options:

  config.action_mailer.default_url_options = { host: 'localhost', port: 3000}
Enter fullscreen mode Exit fullscreen mode

The host and port values only apply to the dev environment, for a production environment make sure to make the necessary changes.

In your routes.rb file include this line:


default_url_options :host => "http://localhost:3000"

Enter fullscreen mode Exit fullscreen mode

Again this is only for the dev environment!


Conclusion

Congratulations! You've successfully integrated Active Storage into your Ruby on Rails application with a React frontend. You can now allow users to upload and display avatars while ensuring the files are in the correct format. Feel free to explore more features and customization options that Active Storage offers.

This is just the beginning. You can extend this functionality to handle more types of file uploads and integrate it into different parts of your application. Happy coding!


More Resources:

Active Record

Setting up a Rails/React Project

Setting up Amazon S3 for Active Storage

Top comments (0)