rails active_storage:install
ActiveStorage
easily create 2 tables active_storage_blobs
and active_storage_attachments
-
active_storage_blobs
save about blob file's information such as:key
,filename
,content_type
,meta_data
,byte_size
,... -
active_storage_attachments
is a join table between record andblobs
, usepolymorphic
technique
rails g scaffold user name:string email: string
class User < ActiveRecord::Base
has_one_attached :avatar
end
ActiveStorage
will inject methods for associations between User
model and ActiveStorage::Blob
and ActiveStorage::Attachment
as well as callbacks for deleting files when delete User
.
Change User
's form and controller:
# app/views/users/_form.html.erb
<div class="field">
<%= form.label :avatar %>
<%= form.file_field :avatar %>
</div>
# app/controllers/users_controller.rb
def user_params
params.require(:user).permit(:email, :name, :avatar)
end
When submit the form, it will pass :avatar
as ActionDispatch::Http::UploadedFile
to User
:
And then, ActiveStorage
will use switch case
to choose right action to perform.
def create_after_upload!(io:, filename:, content_type: nil, metadata: nil, identify: true)
build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
end
Avoid uploading (take a long time) while having an open database transaction
Inside build_after_upload
, ActiveStorage
will trigger upload to store service with a key
https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/blob.rb#L154
key
just a token key which generated by:
class ActiveStorage::Blob < ActiveRecord::Base
has_secure_token :key
end
ActiveStorage
use IO.copy_stream to upload file
-
Where?
ActiveStorage
useskey[0..1]/key[2..3]
folder to store file.
ActiveStorage
just use aws-sdk-s3
to put file with key
:
def upload(key, io, checksum: nil)
instrument :upload, key: key, checksum: checksum do
begin
object_for(key).put(upload_options.merge(body: io, content_md5: checksum))
rescue Aws::S3::Errors::BadDigest
raise ActiveStorage::IntegrityError
end
end
end
ActiveStorage
use store service to generate the URL
for file
def url(key, expires_in:, filename:, disposition:, content_type:)
...
generated_url = object_for(key)...
generated_url
...
end
S3
will handle expiration for us.
- How to implements
expires_in
? When you request theimage
, it will send toActiveStorage::DiskController
withparams[:encoded_key]
, andActiveStorage
use these key to find out the key of image. This key containsexpires_in
value, so only image which is fresh will be return orActiveStorage::DiskController
will return:not_found
.
But what happen when show image http://localhost:3000/users/3
, ActiveStorage
will generate a new URL with new expires_in
For more information about how Rails
encrypt the message with expires_in
: https://medium.com/@michaeljcoyne/rails-5-2-metadata-options-for-messageverifier-and-messageencryptor-79540de86f9b
ActiveStorage
supports image transform with ImageMagick
, just add gem mini_magick
to application's Gemfile
.
Processed image will be saved into storage/va/ri/variants/:original_key/:Digest::SHA256.hexdigest(variation.key)
https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/variant.rb#L71
upload
and download
are just like Blob