DEV Community

Alexandre
Alexandre

Posted on

3

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!

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs