Rails 7 adds direct ActiveStorage::Streaming support


With the advent of ActiveStorage, there have been many improvements to the way Rails stores files. However, there has been little support to how files can be extracted, downloaded or streamed.

While an app can easily support primitive features like downloading, larger files or video files benefit from being streamed. In the era of Netflix, no one wants to wait more than a minute to watch content, it needs to be delivered now!

This is where streaming could be beneficial. While downloading waits until the entire file is loaded in your local machine, streaming is useful to display only chunks of data at a time. This reduces wait times considerably. Let’s see how Rails can help!

If you want to build your own controller that streams from ActiveStorage, it’s helpful to have a method that does the streaming correctly. Some level of support was already being provided by ActiveStorage::BaseController, but it was unextracted and hard to use.

Before

Prior to this update, the ability to stream was scoped out to some obscure looking functions.

If a browser supports the ability to display streaming files, use this

response.headers["Content-Type"] = @model.image.content_type
response.headers["Content-Disposition"] = "inline; #{@model.image.filename.parameters}"

@model.image.download do |chunk|
  response.stream.write(chunk)
end

Notice the need to manually set response headers. This is so that the browser understands the type of file it needs to display. Fortunately, there’s a better way!

After

This commit adds a method to natively support file streaming from the controller. It also introduces the ActiveStorage::Streaming module, that can be included in any controller to get access to #send_blob_stream. It wraps the new ActionController::Base#send_stream method to stream a blob from cloud storage.

Let’s look at an example. Pretend we’re building the next Netflix. Obviously, we would need to stream video. To do this, just simply use the #send_blob_stream method and pass in the video file. We can send disposition as a parameter to this method. In the absence of this parameter, Content-Disposition is set to “inline”.

class VideoController < ApplicationController
  include ActiveStorage::SetBlob, ActiveStorage::Streaming

  def show
    http_cache_forever(public: true) do
      send_blob_stream @video, disposition: params[:disposition]
    end
  end
end

There you have it! A quick and easy way to natively support streaming in your Rails applications.