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
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
Add the following line to your config/application.rb
file:
config.active_storage.variant_processor = :mini_magick
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
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
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
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;
}
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>
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'
Make sure to install the gem:
bundle install
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
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
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
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
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}
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"
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!
Top comments (0)