For my final project at Flatiron School we had to add some new technology to our project that we had never used or learned about before and I decided on using Active Storage with AWS S3 to allow a user to upload an avatar. Upon researching, reading blogs, documentation, and watching online tutorials I found that I was having to use many different resources just to figure out how to get started, and therefor have decided to write a step by step tutorial all in one place for whoever needs to get started with Active Storage.
Active Storage setup
First things first, I recommend reading the active storage documentation in rails or at least referencing it if you find that you are having any issues: https://guides.rubyonrails.org/active_storage_overview.html
but I will lay out the necessary steps for you here as well.
install "libvips" or "ImageMagick" for image analysis and transformations
both of these require the image_processing gem so uncomment it in your Gemfile or add it if you don't see it there already:
gem "image_processing", ">= 1.2"
Active storage uses three tables in your database:
• active_storage_blobs
• active_storage_variant_records
• active_storage_attachmentsto create these tables run "bin/rails active_storage:install" and after migrate them by running "bin/rails db:migrate"
Here is a photo of my schema file for reference for how yours should look (note please ignore the "photo" column in my users table I had this here as a temporary column for when I was working on the project before I had implemented Active Storage)
- Declare active storage services in "config/storage.yml"
In "config/environments/development" tell Active Storage which service to use
config.active_storage.service = :local
To use the S3 service in production add this line of code to "config/environments/production"
config.active_storage.service = :amazon
To use the test service add this line of code to "config/environments/test"
config.active_storage.service = :test
Setting up your Models, Controllers, and Serializers
Since I was adding the ability for a user to upload an avatar when creating an account I added the following line of code to my user model:
has_one_attached :image
this line of code is very important and establishes the association, do not be confused that this will not be reflected as a column in your schema file
Add the "with_attached_image" method to you Users Controller:
and make sure to include ":image" into your user params
Modify your User Serializer like so:
(adding ":image" to attributes and the "image" custom method)
Setting up AWS
make sure your service in config/storage.yml set to "S3"
add the "aws-sdk-s3" gem to your gemfile
gem 'aws-sdk-s3', require: false
Create an AWS account and create a bucket in S3 (you can change your bucket name in "config/storage.yml" to your specific bucket name once we create the bucket, and the region to whatever your specific region once we select it in AWS S3
Once you're logged in click on "S3"
You will be taken to this page:
Fill in the form as so:
Bucket name - yourbucketname
AWS Region - your region
Object Ownership - ACLS disables
Block Public Access settings for this bucket - Block all public access
Bucket Versioning - Disable
Tags - leave empty
Default encryption - Server-side encryption w/ Amazon S3 managed keys(SSE-S3)
Bucket Key - Enable
- for more information and understanding on why you should set "Object Ownership" and Public access settings a certain way I recommend watching this video: https://www.youtube.com/watch?v=UOLpv2f8mz8&t=779s
Once you are done click "create bucket"
Click on the name of your new bucket where you will now be taken to a page where you can edit your settings
Click on permissions and scroll to the Bucket Policy section and click on the "edit" button and then the "Policy Generator" button.
Fill out the form as so:
Select Type of Policy - S3 Bucket Policy
Effect - Allow
Principal - arn:aws:iam::ACCOUNT_ID:root
AWS Service - Amazon S3
Actions - DeleteObject, GetObject, PutObject
Amazon Resource Name (ARN) - arn:aws:s3:::BUCKET_NAME/*
To find your account ID click your username in the top right corner and your account id will appear
you can find your ARN in the properties section of the settings
Once you have filled out the form click "Add Statement" and "Generate Policy" and copy the Policy JSON Document code that pops up and paste it into the field under Edit Bucket Policy and click "Save Changes" here's a photo of mine for reference:
Next, go back to your bucket and go to the permissions section where you can scroll down to CORS(Cross-origin resource sharing) and click the "edit" button. Here's mine for reference:
You can add more methods if you need depending on your app and if you plan to deploy your application you will need to do something like this:
Now that all of this is saved and complete, click on your username in the top right corner and then click on "security credentials" and then "users" on the left side of the page and click "create user" in the top right corner
- The name of the user does not matter or need to be connected to an existing user in your application so I would just name it something generic
Once you enter your user's name click "next" and fill out the form like so:
Permission options - Attach policies directly
Permission Policies - AmazonS3FullAccess
Click "Create User" to save your new user and then click on the name of your new user and then "Security Credentials" where you can scroll down to the Access Keys section and click "Create Access Key"
Select "Application running outside AWS" and add a create description tag called something like "Connect AWS S3 to my_app_name". You will now be able to see your access keys, keep this window open and take a screen shot or download them to a CSV file because once you close this page you wont be able to access them again and you will need them to make connections to your app
In your terminal run: "EDITOR=vim bin/rails credentials:edit"
and edit/update "aws", "access_key_id", and "secret_access_key" with the information in the CSV file you just downloaded after we created the access keys above, once you are done close the file and it will automatically save and you can leave the terminal and you are good to go!
Setting up your Frontend
All of my research led me to use Javascript' formData api as a way to allow the user to upload a photo on the frontend through a form which would then connect to the backend to Active Storage. Here is my signup function and handleSubmit function which is attached to the onSubmit on my form
- When working on this project and troubleshooting I made the mistake of adding a header to my POST method so make sure not to do so
Instead of using "e.target.value" like you are most likely using in the rest of your form you will instead need to use "e.target.files" on the image (I am using React Bootstrap if you are confused as to why my form looks like like this)
If you are using validations in your project I found that the way params was sent back for the image using formData was creating some issues. To resolve this I had to create frontend validations for the image in the form as a workaround to prevent my app from breaking. I am sure there are alternative ways to do this, this is just the option that worked for me. In order to do this I created a new state object:
and then a conditional statement: "isImage ?" in my form like so:
Conclusion
I hope this was helpful, of course aspects of this tutorial are specific to my project but I think they can easily be adjusted and manipulated to the specifics of yours. I tried to get into the obstacles and troubleshooting I had to figure out along the way incase you find yourself in the same position!
Top comments (0)