1. Introduction
If before Rails version 5.2, to upload files, most of us always need a 3rd party library, one of which is the most famous one is CarrierWave gem – it is a very stable gem and is believed by the community. use the most. But from Rails 5.2 onwards, we have another option is Active Storage . Unlike CarrierWave, Active Storage is built into Rails or in other words it is developed by Rails, so we are completely assured when using
2. Installation
Migration
Although Active Storage is built into Rails, by default it is not installed when running rails new. To install Active Storage we run the following command:
1 2 3 | rails active_storage <span class="token symbol">:install</span> rails db <span class="token symbol">:migrate</span> |
After running these two commands, it will create two tables in the DB named active_storage_blobs and active_storage_attachments . By default, Active Storage uses a Polymorphic relation in rails, active_storage_blobs contains file information, and active_storage_attachments is a polymorphic join table that stores the model’s class name.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <span class="token keyword">class</span> <span class="token class-name">CreateActiveStorageTables</span> <span class="token operator"><</span> <span class="token constant">ActiveRecord</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token constant">Migration</span> <span class="token punctuation">[</span> <span class="token number">5.2</span> <span class="token punctuation">]</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">change</span></span> create_table <span class="token symbol">:active_storage_blobs</span> <span class="token keyword">do</span> <span class="token operator">|</span> t <span class="token operator">|</span> t <span class="token punctuation">.</span> string <span class="token symbol">:key</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> string <span class="token symbol">:filename</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> string <span class="token symbol">:content_type</span> t <span class="token punctuation">.</span> text <span class="token symbol">:metadata</span> t <span class="token punctuation">.</span> bigint <span class="token symbol">:byte_size</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> string <span class="token symbol">:checksum</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> datetime <span class="token symbol">:created_at</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> index <span class="token punctuation">[</span> <span class="token symbol">:key</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> unique <span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token keyword">end</span> create_table <span class="token symbol">:active_storage_attachments</span> <span class="token keyword">do</span> <span class="token operator">|</span> t <span class="token operator">|</span> t <span class="token punctuation">.</span> string <span class="token symbol">:name</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> references <span class="token symbol">:record</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">,</span> polymorphic <span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> index <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> references <span class="token symbol">:blob</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> datetime <span class="token symbol">:created_at</span> <span class="token punctuation">,</span> null <span class="token punctuation">:</span> <span class="token boolean">false</span> t <span class="token punctuation">.</span> index <span class="token punctuation">[</span> <span class="token symbol">:record_type</span> <span class="token punctuation">,</span> <span class="token symbol">:record_id</span> <span class="token punctuation">,</span> <span class="token symbol">:name</span> <span class="token punctuation">,</span> <span class="token symbol">:blob_id</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> name <span class="token punctuation">:</span> <span class="token string">"index_active_storage_attachments_uniqueness"</span> <span class="token punctuation">,</span> unique <span class="token punctuation">:</span> <span class="token boolean">true</span> t <span class="token punctuation">.</span> foreign_key <span class="token symbol">:active_storage_blobs</span> <span class="token punctuation">,</span> column <span class="token punctuation">:</span> <span class="token symbol">:blob_id</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
Setting
We implement the Declaration of Active Storage services in config / storage.yml . For each service our application uses, provide the required name and configuration. The example below declares three services named local, test and amazon :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | local <span class="token punctuation">:</span> service <span class="token punctuation">:</span> <span class="token constant">Disk</span> root <span class="token punctuation">:</span> <span class="token operator"><</span> <span class="token string">%= Rails.root.join("storage") %> test: service: Disk root: <%=</span> <span class="token constant">Rails</span> <span class="token punctuation">.</span> root <span class="token punctuation">.</span> join <span class="token punctuation">(</span> <span class="token string">"tmp/storage"</span> <span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token operator">></span> amazon <span class="token punctuation">:</span> service <span class="token punctuation">:</span> <span class="token constant">S3</span> access_key_id <span class="token punctuation">:</span> <span class="token string">""</span> secret_access_key <span class="token punctuation">:</span> <span class="token string">""</span> bucket <span class="token punctuation">:</span> <span class="token string">""</span> region <span class="token punctuation">:</span> <span class="token string">""</span> <span class="token comment"># e.g. 'us-east-1'</span> |
Let Active Storage know which service to use by setting Rails.application.config.active_storage.service corresponding to each environment because each environment will probably use a different service.
For example, to use the Disk service in a development environment, we will add the following to config / environment / development.rb :
1 2 | config <span class="token punctuation">.</span> active_storage <span class="token punctuation">.</span> service <span class="token operator">=</span> <span class="token symbol">:local</span> |
Or using s3 in production , I set it at config / environments / production.rb :
1 2 | config <span class="token punctuation">.</span> active_storage <span class="token punctuation">.</span> service <span class="token operator">=</span> <span class="token symbol">:amazon</span> |
3. Use
Active Storage allows us to attach one or more files to a record using the has_one_attached and has_many_attached macros, respectively.
To illustrate, we consider the following example, a user has only 1 avatar or a product has many images. To perform that example we need to create the User and Product models as follows:
1 2 3 4 | rails g model <span class="token constant">User</span> name <span class="token symbol">:string</span> email <span class="token symbol">:string</span> rails g model <span class="token constant">Product</span> name <span class="token symbol">:string</span> price <span class="token symbol">:integer</span> rails db <span class="token symbol">:migrate</span> |
has_one_attached
Inside User model:
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> has_one_attached <span class="token symbol">:avatar</span> <span class="token keyword">end</span> |
Inside controller and view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">class</span> <span class="token class-name">UsersController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">new</span></span> <span class="token variable">@user</span> <span class="token operator">=</span> <span class="token constant">User</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">create</span></span> user <span class="token operator">=</span> <span class="token constant">User</span> <span class="token punctuation">.</span> create params <span class="token punctuation">.</span> <span class="token keyword">require</span> <span class="token punctuation">(</span> <span class="token symbol">:user</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> permit <span class="token punctuation">(</span> <span class="token symbol">:name</span> <span class="token punctuation">,</span> <span class="token symbol">:email</span> <span class="token punctuation">,</span> <span class="token symbol">:avatar</span> <span class="token punctuation">)</span> redirect_to user <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">show</span></span> <span class="token variable">@user</span> <span class="token operator">=</span> <span class="token constant">User</span> <span class="token punctuation">.</span> find params <span class="token punctuation">[</span> <span class="token symbol">:id</span> <span class="token punctuation">]</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token comment"># views/users/new.html.erb</span> <span class="token operator"><</span> <span class="token string">%= form_for(@user) do |f| %> <div class=</span> <span class="token string">"field"</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token string">%= f.label "Name" %><br /> <%=</span> f <span class="token punctuation">.</span> text_field <span class="token symbol">:name</span> <span class="token string">%> </div></span> <span class="token operator"><</span> div <span class="token keyword">class</span> <span class="token operator">=</span> <span class="token string">"field"</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token string">%= f.label "Email" %><br /> <%=</span> f <span class="token punctuation">.</span> email_field <span class="token symbol">:email</span> <span class="token string">%> </div></span> <span class="token operator"><</span> div <span class="token keyword">class</span> <span class="token operator">=</span> <span class="token string">"field"</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token string">%= f.label "Avatar" %><br /> <%=</span> f <span class="token punctuation">.</span> file_field <span class="token symbol">:avatar</span> <span class="token string">%> </div></span> <span class="token operator"><</span> <span class="token operator">%</span> <span class="token operator">=</span> f <span class="token punctuation">.</span> submit <span class="token string">"submit"</span> <span class="token string">%> <% end %></span> |
1 2 3 4 5 | <span class="token comment"># views/users/show.html.erb</span> <span class="token constant">Name</span> <span class="token punctuation">:</span> <span class="token operator"><</span> <span class="token string">%= @user.name %><br> Email: <%=</span> <span class="token variable">@user</span> <span class="token punctuation">.</span> email <span class="token string">%><br></span> <span class="token constant">Avatar</span> <span class="token punctuation">:</span> <span class="token operator"><</span> <span class="token operator">%</span> <span class="token operator">=</span> image_tag <span class="token variable">@user</span> <span class="token punctuation">.</span> avatar <span class="token operator">%</span> <span class="token operator">></span> |
Thus, we can create avatars for users without creating more avatar fields in users table or any other table. In addition, to attach an avatar to an existing user, we can use user.avatar.attach (params [: avatar]) or check if the user has been attached avatar by using user.avatar.attached?
has_many_attached
The product has many images:
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">Product</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> has_many_attached <span class="token symbol">:images</span> <span class="token keyword">end</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">class</span> <span class="token class-name">ProductsController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">new</span></span> <span class="token variable">@product</span> <span class="token operator">=</span> <span class="token constant">Product</span> <span class="token punctuation">.</span> <span class="token keyword">new</span> <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">create</span></span> product <span class="token operator">=</span> <span class="token constant">Product</span> <span class="token punctuation">.</span> create <span class="token operator">!</span> <span class="token punctuation">(</span> params <span class="token punctuation">.</span> <span class="token keyword">require</span> <span class="token punctuation">(</span> <span class="token symbol">:product</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> permit <span class="token punctuation">(</span> <span class="token symbol">:name</span> <span class="token punctuation">,</span> images <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> redirect_to product <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">show</span></span> <span class="token variable">@product</span> <span class="token operator">=</span> <span class="token constant">Product</span> <span class="token punctuation">.</span> find params <span class="token punctuation">[</span> <span class="token symbol">:id</span> <span class="token punctuation">]</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token comment"># views/products/new.html.erb</span> <span class="token operator"><</span> <span class="token string">%= form_for(@product) do |f| %> <div class=</span> <span class="token string">"field"</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token string">%= f.label "Name" %><br /> <%=</span> f <span class="token punctuation">.</span> text_field <span class="token symbol">:name</span> <span class="token string">%> </div></span> <span class="token operator"><</span> div <span class="token keyword">class</span> <span class="token operator">=</span> <span class="token string">"field"</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token string">%= f.label "Image" %><br /> <%=</span> f <span class="token punctuation">.</span> file_field <span class="token symbol">:images</span> <span class="token punctuation">,</span> multiple <span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token string">%> </div></span> <span class="token operator"><</span> <span class="token operator">%</span> <span class="token operator">=</span> f <span class="token punctuation">.</span> submit <span class="token string">"submit"</span> <span class="token string">%> <% end %></span> |
1 2 3 4 5 6 7 8 9 | <span class="token comment"># views/products/show.html.erb</span> <span class="token constant">Name</span> <span class="token punctuation">:</span> <span class="token operator"><</span> <span class="token string">%= @product.name %><br> Images:<br> <% @product.images.each do |image| %> <%=</span> image_tag image <span class="token string">%> <br></span> <span class="token operator"><</span> <span class="token operator">%</span> <span class="token keyword">end</span> <span class="token operator">%</span> <span class="token operator">></span> |