ActiveStorage now pre-processes PDFs and videos

ActiveStorage does so many things out of the box. It handles file uploads, storage, and processing. It also provides a preview of the files. These are known as file representations.

You can call representation on an attachment to display an image variant, or a preview of a video or PDF. If a file can be represented then #representable? will return true. This is because files such as Word or Excel documents cannot be represented.

Internally, #representation checks if a file is an image, and if it is, returns a “variant”. If it is a PDF or a video, it creates a “preview”, which is a PNG image of the first page of the PDF or the first frame of the video. If a file can be previewed, #previewable? will return true.

These representations can either be created on the fly or pre-processed. Pre-processing is done by a background job, ActiveStorage::TransformJob which is run when the file is attached or updated.

Let’s use an example Book model with attributes name and cover to understand this Rails update better.

class Book < ApplicationRecord
  has_one_attached :cover do |attachable|
    attachable.variant :thumb, resize_to_limit: [100, 100], preprocessed: true
  end
end

Before

Now when an image is uploaded, the ActiveStorage::TransformJob is run and the image is pre-processed. A variant is created and stored. When the image is requested, the variant is returned.

Performed ActiveStorage::TransformJob (Job ID: ce4eff9c-f862-4d19-bd87-42b6562249b0) from Async(default) in 98.52ms

However when a PDF or a video is uploaded, the ActiveStorage::TransformJob throws an ActiveStorage::InvariableError exception.

Error performing ActiveStorage::TransformJob (Job ID: 97495de3-5be4-41e2-8ed9-b5fc8fd12e7f) from Async(default) in 67.61ms: ActiveStorage::InvariableError (ActiveStorage::InvariableError):
/Users/swaathi/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/activestorage-7.1.2/app/models/active_storage/blob/representable.rb:38:in `variant'
...
/Users/swaathi/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/activejob-7.1.2/lib/active_job/execution.rb:24:in `perform_now'
...
/Users/swaathi/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/activestorage-7.1.2/app/models/active_storage/blob/representable.rb:38:in `variant': ActiveStorage::InvariableError (ActiveStorage::InvariableError)

A quick Google search says that the exception is raised when ActiveStorage::Blob#variant is called on a blob that isn’t variable.

ActiveStorage::TransformJob looks like this:

def perform(blob, transformations)
  blob.variant(transformations).processed
end

Since PDFs and videos are not images, variants do not exist. Instead, they first need to be converted into an image before image transformations can be applied. This is why the exception is raised.

After

With this Rails update, ActiveStorage::TransformJob now pre-processes PDFs and videos. This PR requests representations of the blob, instead of variants. Once an image equivalent of the PDF or video is created, the image transformations are then applied.

def perform(blob, transformations)
  blob.representation(transformations).processed
end

Now when a PDF or a video is uploaded, previews can be pre-processed in the background, instead of on the fly. This is a huge performance improvement.

Need help on your Ruby on Rails or React project?

Join Our Newsletter