DEV Community

Alexandre
Alexandre

Posted on

Uma introdução de Active Storage em Rails 7

Se você quer que sua aplicação Rails seja capaz de lidar arquivos, como imagens, videos, pdfs, etc. Você está no lugar certo.

Hoje, nós vamos aprender sobre Active Storage.

O que é Active Storage

Active Storage é uma ferramenta que facilita o upload de arquivos para serviço de armazenamento em nuvem como Amazon S3, Google Cloud Storage ou Microsoft Azure e anexa esses arquivos a objetos Active Record. Ele também vem com um serviço baseado em disco local para desenvolvimento e teste e suporta espelhamento de arquivos para serviços subordinados para backups e migrações.

Observação: Eu não vou mostrar como que integrar o Active Storage com o serviço de armazenamento em nuvem. Eu só vou focar em ambiente de desenvolvimento.

Mas, antes de instalar Active Storage na sua aplicação, é necessário alguns software de terceiro para ter suporte em analise e processamento de arquivos.

Eu vou deixar uma lista de software.

  • libvips v8.6+ ou ImageMagick para analise e transformação de imagem.
  • ffmpeg v3.4+ para pré-visualizações de vídeo e ffprobe para analise de video/audio
  • poppler ou muPDF para pré-visualizações de PDF

E também é necessário colocar uma gem image_processing, se sua aplicação vai usar arquivo do tipo imagem.

Antes de instalar e usar software de terceiros, certifique-se de compreender as implicações de licenciamento de fazê-lo. O MuPDF, em particular, é licenciado sob AGPL e requer uma licença comercial para alguns usos.

Eu vou ensinar a instalar o Active Storage criando uma aplicação, porque eu acho muito mais melhor você ver a construção da aplicativo.

Então vamos criar aplicação

Criando uma aplicação

A aplicação que eu vou criar vai ser um simples armazenamento de arquivos, em que vai ter dois models e controllers, Usuario e Arquivo.

rails new armazenamento
cd armazenamento
Enter fullscreen mode Exit fullscreen mode

Vamos colocar uma gem no Gemfile para que possa ter suporte de imagem.

echo "gem 'image_processing'" >> Gemfile
bundle install
Enter fullscreen mode Exit fullscreen mode

Com gem instalado, vamos instalar o Active Storage.

rails active_storage:install
Enter fullscreen mode Exit fullscreen mode

Esse comando cria uma migração, em que tem três tabelas active_storage_blobs, active_storage_attachments e active_storage_variant_records.

Eu não vou explicar cada um dele, mas saiba que são essas tabelas que vai armazenar os arquivos.

Como ele gerou uma migração, vamos migrar ele.

rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Agora, vamos criar os models e os controllers.

Antes de criar um model, tenho que falar que existe duas maneiras de anexar o arquivo no model, uma é usando o generator e outro é colocando o código na mão, o que não é complicado de fazer.

Primeiro, vamos usar o generator no model Usuario.

rails g model Usuario email senha avatar:attachment
Enter fullscreen mode Exit fullscreen mode

Como você pode ver o tipo attachment, ele indica que esse model vai ter um anexo, ou seja, vai ter somente um arquivo, se você quer que seja vários arquivos, basta usar o tipo attachments.

Beleza, vamos ver o que ele gerou para nós, vá para o arquivo app/models/usuario.rb.

# app/models/usuario.rb
class Usuario < ApplicationRecord
  has_one_attached :avatar
end
Enter fullscreen mode Exit fullscreen mode

O has_one_attached é uma macro que configura uma relação um para um entre o registro e o arquivo, ou seja, cada registro só pode ter um arquivo.

Isso é perfeito para gente, pois nós queremos que seja uma imagem do usuário.

E o que generator gerou foi isso, ou seja, se eu não quiser usar o generator, basta colocar esse código no model e pronto.

Então, vamos criar um model sem usar o tipo attachment ou attachments e colocar o código na mão.

rails g model Arquivo 
Enter fullscreen mode Exit fullscreen mode

Agora abra no arquivo app/models/arquivo.rb para colocar o código.

# app/models/arquivo.rb
class Arquivo < ApplicationRecord
  has_many_attached :arquivos
end
Enter fullscreen mode Exit fullscreen mode

Você pode ter percebido que eu escrevi diferente do código de cima.

O has_many_attached é uma macro que configura uma relação de um para muitos entre o registro e os arquivos, ou seja, cada registro pode ter mais de um arquivo.

Viu, é simples implementar sem o generator.

Agora que criamos os models, vamos migrar eles.

rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Com models criado e migrado, vamos criar os controllers.

rails g controller Usuarios index show new edit
rails g controller Arquivos index show new edit
Enter fullscreen mode Exit fullscreen mode

Agora vamos editar esses controllers. vamos primeiro para app/cotrollers/usuarios_controller.rb.

# app/cotrollers/usuarios_controller.rb
class UsuariosController < ApplicationController
  before_action :define_usuario, except: [:index, :new, :create]

  def index
    @usuarios = Usuario.all
  end

  def show
  end

  def new
    @usuario = Usuario.new
  end

  def create
    @usuario = Usuario.new(usuario_params)

    if @usuario.save
      redirect_to usuario_path(@usuario)
    else
      render :new
    end
  end

  def edit
  end

  def update
    if @usuario.update(arquivo_params)
      redirect_to usuario_path(@usuario)
    else
      render :edit
    end
  end

  def destroy
    if @usuario.destroy
      redirect_to usuarios_path
    end
  end

  private 

  def usuario_params
    params.require(:usuario).permit(:email, :senha, :avatar)
  end

  def define_usuario
    begin
      @usuario = Usuario.find(params[:id])
    rescue ActiveRecord::RecordNotFound
      redirect_to usuarios_path, status: :not_found
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Como você viu o código que eu escrevi, não tem nada demais, ou seja, a imagem vai ser salva, se o usuário colocar uma imagem no form.

Só que a história muda um pouquinho, quando nós estamos lidando em que aceita vários arquivos.

Então abra o arquivo app/controllers/arquivos_controller.rb e coloque o código abaixo.

# app/controllers/arquivos_controller.rb
class ArquivosController < ApplicationController
  before_action :define_arquivo, except: [:index, :new, :create]

  def index
    @arquivos = Arquivo.all
  end

  def show
  end

  def new
    @arquivo = Arquivo.new
  end

  def create
    @arquivo = Arquivo.new(arquivo_params)

    if @arquivo.save
      redirect_to arquivo_path(@arquivo)
    else
      render :new
    end
  end

  def edit
  end

  def update
    if @arquivo.update(arquivo_params)
      redirect_to arquivo_path(@arquivo)
    else
      render :edit
    end
  end

  def destroy
    if @arquivo.destroy
      redirect_to arquivos_path
    end
  end

  private 

  def arquivo_params
    params.require(:arquivo).permit(arquivos: [])
  end

  def define_arquivo
    @arquivo = Arquivo.where(id: params[:id]).includes(arquivos_attachments: :blob)
    @arquivo = @arquivo[0]
    redirect_to arquivos_path, status: :not_found if @arquivo.nil?
  end
end
Enter fullscreen mode Exit fullscreen mode

Se você observar o código, talvez você vai ver algo que nunca tenha visto antes, percebeu?

Se não percebeu, observe no método arquivo_params e me diz o que tem diferente do controller Usuario e Arquivo?

No controller usuario, o avatar está igual aos outros atributos, enquanto no controller arquivo, ele está com um array.

Então, lembre-se de que se você estiver lidando com muitos arquivos, coloque o array, se não, use um symbol.

E também outra diferença, no método define_arquivo, eu utilizei o where e o includes. O motivo de fazer isso foi para evitar o problema de n+1, se não sabe sobre esse problema, eu recomendo que pesquise sobre isso.

Agora, com controllers criado, vamos editar as rotas, abra o arquivo config/routes.rb.

# config/routes.rb
Rails.application.routes.draw do
  resources :arquivos
  resources :usuarios
end
Enter fullscreen mode Exit fullscreen mode

Com models, controllers e rotas editado, agora faltam os views.

Primeiro, vamos editar views de usuarios.

<!-- app/views/usuarios/index.html.erb -->
<% @usuarios.each do |usuario| %>
  <%= link_to usuario.email, usuarios_path(usuario)%>
<% end %>

<%= link_to "new", new_usuario_path %>
Enter fullscreen mode Exit fullscreen mode
<!-- app/views/usuarios/new.html.erb -->
<%= form_with model: @usuario, url: usuarios_path do |form| %>
  <%= form.label :email %>
  <%= form.text_field :email %>

  <%= form.label :senha %>
  <%= form.password_field :senha %>

  <%= form.label :avatar %>
  <%= form.file_field :avatar %>

  <%= form.submit %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Para que possa enviar um arquivo a partir de um form, você pode usar o file_field.

<!-- app/views/usuarios/show.html.erb -->
avatar: <%= image_tag(@usuario.avatar) %> <br>
email: <%= @usuario.email %>
Enter fullscreen mode Exit fullscreen mode

Para poder exibir uma imagem, você pode usar o image_tag.

Agora rode o servidor e vai para url localhost:3000/usuarios e crie um usuario com imagem e veja a coisa funciona.

Agora, vamos editar os views de arquivos.

<!-- app/views/arquivos/index.html.erb -->
<% @arquivos.each do |arquivo| %>
  <%= link_to arquivo.id, arquivo_path(arquivo)%>
<% end %>

<%= link_to "new", new_arquivo_path %>
Enter fullscreen mode Exit fullscreen mode
<!-- app/views/arquivos/new.html.erb -->
<%= form_with model: @arquivo, url: arquivos_path do |form| %>
  <%= form.label :arquivos %>
  <%= form.file_field :arquivos, multiple: true %>

  <%= form.submit %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Pode perceber que file_field é diferente do usuario, o multiple: true indica que esse campo aceita vários arquivos.

<!-- app/views/arquivos/show.html.erb -->
<% @arquivo.arquivos.each do |arquivo| %>
  <% if arquivo.content_type.start_with? "image" %>
    <%= image_tag arquivo %>
  <% elsif arquivo.content_type.start_with? "video" %>
    <%= video_tag( url_for(arquivo), controls: true ) %>
        <!-- o video_tag recebe o primeiro argumento uma url do arquivo e esse controls é para ativar o controle do video -->
  <% else %>
    <!-- Se não for uma imagem e nem video, baixe ele -->
    <%= link_to(arquivo.filename, rails_blob_path(arquivo, disposition: "attachment")) %>
    <!-- o arquivo.filename mostra o nome do arquivo -->
    <!-- o rails_blob_path cria um link de download -->
  <% end %>
  <br>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Agora vá para url localhost:3000/arquivos e adicione vários arquivos de tipo diferente.

Você pode notar, se você colocou um arquivo que não seja imagem e video, ao clicar no arquivo, ele vai fazer download.

E nós acabamos por aqui.

Então, espero que tenha ajudado e tchau!

Top comments (0)