PresignedUpload
Control file uploads via presigned URLs to cloud storage services.
A presigned URL is generated by a server that grants temporary and controlled access to a specific resource. In the context of file uploads, it allows clients to directly upload files to a designated storage location without the server being directly involved in the transfer process.
Installation
1. Add the gem into your project
Add this to your Gemfile and run bundle install
.
gem 'presigned_upload'
Or install it yourself as:
gem install presigned_upload
2. Create the initializer file
Run the generator to create the initializer file:
rails g presigned_upload:install
3. Generate Uploadable Model
Use the PresignedUpload generator to create your uploadable model.
This model is intended to be a representation of access to a file, and also its basic informations.
For example, if you want create a UploadLink model, you can use:
rails g presigned_upload:uploadable_model UploadLink
If your database uses UUID for the primary and foreign keys, you can pass the --uuid
option:
rails g presigned_upload:uploadable_model UploadLink --uuid
The generator will create the model with the given name and the migration file:
# app/models/upload_link.rb
class UploadLink < ApplicationRecord
presigned_uploadable_model
end
# This columns are required, but you can add more if you need.
# For example, you can create a column called 'storage_size'
# to save the storage size of the file
class CreateUploadLinks < ActiveRecord::Migration[7.0]
def change
create_table :upload_links, id: :uuid do |t|
t.string :original_name, null: false
t.string :content_type, null: false
t.string :upload_status, null: false
t.timestamps
end
end
end
By default, an uploadable model contains:
-
original_name
: The original name of the file -
content_type
: The content-type of the file (Utile for validations) -
upload_status
: The status of the upload process [initial
,completed
]
4. Run the migration
rails db:migrate
Usage
Configuring initializer
You can configure the presigned_upload initializer to set the storage service and its options.
For AWS S3 for example, you going to need to add the storage
to :aws
and inform the bucket
where you want to create presigned URLs in order to upload and access files:
PresignedUpload.configure do |config|
config.storage = :aws
config.storage_options = {
bucket: 'my_bucket'
}
end
Important: For an AWS configuration, you need to add the AWS SDK for Ruby in order to PresignedUpload
gem to work correctly:
gem 'aws-sdk-s3'
If you don't add this, an error will be raised when trying to run Rails.
Uploadable Model
The uploadable model will be used to access files in the cloud storage services. Below there are some of the methods that can be accessed:
# Returns a presigned URL for uploading the file
# Only returns the URL if the upload_status is 'initial'
upload_link.upload_url
# Returns a presigned URL for accessing the stored file
# Only returns the URL if the upload_status is 'completed'
upload_link.url
# Deletes the stored file from the cloud
# This method is automatically called in before_destroy
# callback when you destroy the record
upload_link.delete_stored_file
In order to be able to use this methods, you need to configure the store directory where the file will be stored.
Storage path
By default, PresignedUpload inserts the method presigned_uploadable_model
in your Model, that sets the value of the store_dir
to: "uploads/#{model_name.plural}/#{id}"
.
The final store_path
of the file in the cloud will be the junction between store_dir
+ original_name
.
So, in the example below, the file will be stored as uploads/upload_links/123/my_file.txt
:
class UploadLink < ApplicationRecord
presigned_uploadable_model
end
upload_link.id = '123'
upload_link.original_name = 'my_file.txt'
upload_link.store_path
# => uploads/upload_links/123/my_file.txt
Changing the storage directory
The presigned_uploadable_model
method accepts the :store_dir
option, that can be a Symbol
, a String
or a Proc
.
Using a string:
class UploadLink < ApplicationRecord
presigned_uploadable_model store_dir: '/uploads/my/custom/path'
end
upload_link.original_name = 'my_file.txt'
upload_link.store_path
# => "/uploads/my/custom/path/my_file.txt"
Using a Proc:
class UploadLink < ApplicationRecord
presigned_uploadable_model store_dir: -> { "/uploads/upload_links/#{id}" }
end
upload_link.id = '123'
upload_link.original_name = 'my_file.txt'
upload_link.store_path
# => "/uploads/upload_links/123/my_file.txt"
Using a Symbol:
class UploadLink < ApplicationRecord
presigned_uploadable_model store_dir: :my_custom_dir
# Using as a symbol, the code will expect a instance method with same name
def my_custom_dir
"/my/custom/dir/#{id}"
end
end
upload_link.id = '123'
upload_link.original_name = 'my_file.txt'
upload_link.store_path
# => "/my/custom/dir/123/my_file.txt"
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/denisstael/presigned_upload.
License
The gem is available as open source under the terms of the MIT License.