Action Text - Handling file attachments (Audio / Video / PDF) in Rails 6

By Ronak Gothi On January 11 2021

Action Text is a very powerful rails feature which uses Trix editor from basecamp under the hood, In this article we will understand how to use ActionText to work with different media format.

Let's create a new Blog Application for this demo.

1
2
rails new action-text-demo -d postgresql
rails g scaffold Blog

Install action text into your app

1
2
bin/rails action_text:install
bin/rails db:create db:migrate RAILS_ENV=development

In the blog model add a field content which is a rich_text

1
2
3
class Blog < ApplicationRecord
  has_rich_text :content
end

Note: the content field is managed by the table active_storage_attachments, hence blog table do not have to create the field content seperately.

In the views add the form field

1
2
3
4
5
6
  <%# app/views/messages/_form.html.erb %>
  <div class="field">
    <%= form.label :content %>
    <%= form.rich_text_area :content %>
  </div>

Display the rich_text content in /blog/show

1
2
<%# app/views/blogs/show.html.erb %>
<%= @blog.content %>

Permit the content attribute in the controller

1
2
3
4
#app/controllers/blogs_controller.rb
def blog_params
  params.require(:blog).permit(:content)
end

Visit http://localhost:3000/blogs/new We would see the following screen

ActionText-Demo

Now let's try uploading

  1. Video
  2. Image
  3. PDF
  4. Docx
  5. Audio

ActionText-Demo

You'll notice the following error because, ActiveText needs image_processing gem to generate preview image of the uploaded images.

1
LoadError (Generating image variants require the image_processing gem. Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.):

Let's install the gem and try again, and it works.

ActionText-Demo

However, we have the following issue

  1. Video is not playable.
  2. PDF generates a preview of the first page, i.e, rest of the document cannot be readily be consumed.
  3. Audio and Docx displays no preview and appears broken.

Let's edit the active_storage blob file to generate content as per the file uploaded instead of using the default image tags.

  1. HTML5 <video> tag if the blob is of type Video
  2. HTML5 audio tag if the blob is of type Audio.
  3. HTML5 embed tag if the blob is of type PDF.
  4. iFrame for docx upload.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<%# www/app/views/active_storage/blobs/_blob.html.erb %>
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
  <% if blob.video? %>
    <video
      controls="false"
      width="100%"
      poster=<%= polymorphic_url(blob.representation(resize_to_limit: [ 1024, 768 ])) %>
    >
      <source src= <%= rails_blob_url(blob) %>, type=<%= blob.content_type %> >
    </video>
  <% elsif blob.audio? %>
    <audio controls="true" width="100%" preload="metadata">
      <source src= <%= rails_blob_url(blob) %>, type=<%= blob.content_type %> >
    </audio>
  <% elsif blob.content_type == 'application/pdf' %>
    <embed src=<%= rails_blob_url(blob) %> width="800" height="500"
           type="application/pdf">
  <% elsif blob.content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' %>
    <iframe src='https://view.officeapps.live.com/op/embed.aspx?src=<%= rails_blob_url(blob) %>' width='100%' height='500px' frameborder='0'>
    </iframe>
<% elsif blob.representable? %>
    <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
  <% end %>

  <figcaption class="attachment__caption">
    <% if caption = blob.try(:caption) %>
      <%= caption %>
    <% else %>
      <span class="attachment__name"><%= blob.filename %></span>
      <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
    <% end %>
    <span> ยท </span>
    <%= link_to "Download", rails_blob_path(blob, disposition: "attachment") %>
  </figcaption>
</figure>

Add the above tags to ActionText::ContentHelper.allowed_tags list

1
2
3
4
5
6
7
8
9
10
11
12
    #config/application.rb
    config.after_initialize do
      ActionText::ContentHelper.allowed_attributes.add 'style'
      ActionText::ContentHelper.allowed_attributes.add 'controls'
      ActionText::ContentHelper.allowed_attributes.add 'poster'

      ActionText::ContentHelper.allowed_tags.add 'video'
      ActionText::ContentHelper.allowed_tags.add 'audio'
      ActionText::ContentHelper.allowed_tags.add 'source'
      ActionText::ContentHelper.allowed_tags.add 'embed'
      ActionText::ContentHelper.allowed_tags.add 'iframe'
    end

And the uploaded media uploaded via Rails ActionText content is now viewable as embedded content.

ActionText-Demo

  1. Picture appears as uploaded
  2. Video is now playable with preview image
  3. PDF now appears with content embedded via browsers embed tag
  4. Audio is now playable
  5. Docx is not viewable via an Iframe