In our project, we are using Active Storage as our upload solution, and Amazon S3 for Storage service. Recently because of a requirement in the project, we decided to encrypt files before uploading to S3 and decrypting them after downloading (client-side encryption). Amazon S3 Gem meets this requirement and we can find the documentation through this link.
And the following greate article shows how we can implement it in a transparent way:
Active Storage S3 Client-Side Encryption
Use client-side encryption to encrypt your data before sending it to S3. You can provide an encryption key to use…
This blog post has described what you need, BUT if you want to have a preview for the uploaded files, so you are the one who should read this article.
Preview for Uploaded Files (image, PDF, movie, …)
Unfortunately, Active Storage does not completely support preview for client-side encrypted files, if we use the default previewer path which is created by Active Storage itself, it will give us a URL that refers directly to the encrypted thumbnail image on the S3, and it’s not previewable by an HTML image tag.
NOTE: We should not forget that we need a transparent way that works with any configuration of Active Storage. For example,
config.active_storage.service = :local should work properly in your development environment while
config.active_storage.service = :amazon works on the production server.
Assume that we have an
Attachment model that is associated with on document:
class Attachment < ApplicationRecord
Now, we will go through the following steps:
- The blog post in the previous section has missed an important line of code that is used in the Active Storage Preview, and we need to change the download method like the following code:
def download(key, &block)
binary_data = instrument :download, key: key do
end yield binary_data if block binary_data
The bold line is the missing line. If you want to know why we need to add this, check out this line of the Active Storage source code.
2. Then we are going to create a service object in order to process and download preview blob:
@attachment = attachment
end def call
.processed if document_preview.is_a?(ActiveStorage::Variant)
variant = document_preview
variant = ActiveStorage::Variant.new(
variant_preview = variant.processed
3. Now we need to create a controller to serve preview file:
class AttachmentsController < ApplicationController
preview_data = ::Attachments::Preview.new(attachment).call
end private def attachment
@attachment ||= ::Attachment.find params[:id]
4. And that’s it, now you just need to use the proper route to implemented action in your
<img /> tags:
<img src=<%= preview_attachment_path(attachment) %> />
If you want to use the Preview feature of the Active Storage you need to know that it generates a thumbnail beside the original file, but when you are using client-side encryption, you can not directly refer to the thumbnail file (because it’s encrypted too). In this case, you need to decrypt the thumbnail file, before previewing. We have explained how you can meet this requirement by creating a custom controller and use it in your image tag instead of the URL which is generated by the Active Storage.