Múltiplos uploads com ActiveStorage

Tutoriais - 08/Jul/2020 - por André Kanamura

O Active Storage do framework Rails facilita o envio de arquivos para serviços de armazenamento na nuvem como Amazon S3 ou Google Cloud, basta configurá-lo seguindo alguns poucos passos do guia oficial. Vamos mostrar aqui um exemplo simplificado de aplicação de galeria de fotos que permite upload de múltiplas imagens simultaneamente.

Aviso: vamos apresentar um exemplo bastante simplificado de aplicação para facilitar a compreensão do passo a passo. Consequentemente, a estrutura do sistema não é a mais adequada para uma aplicação real.

Em nosso exemplo, temos uma classe Album que pode receber muitas fotos na forma de anexos, para isso incluímos o has_many_attached, que estabelece uma relação de "um para muitos" entre album e photos:

class Album < ApplicationRecord
  has_many_attached :photos
end

Digamos que o model Album possua um título, além das fotos que forem anexadas. Assim, a view do formulário de criação de um álbum poderia ficar assim:

<h1>Novo Álbum</h1>

<%= form_with model: @album do |f| %>
  <%= f.label :title, 'Título' %>
  <%= f.text_field :title %>
  <%= f.label :photos, 'Fotos' %>
  <%= f.file_field :photos, multiple: true %>
  <%= f.submit 'Enviar'  %>
<% end %>

Note como o campo file_field do formulário possui o atributo multiple: true para que seja possível enviar mais de um arquivo simultaneamente. Vale lembrar que devemos permitir no controller de albums um array de photos nos params:

  params.require(:album).permit(:title, :tag, photos:[])

Agora os arquivos anexados à uma instância de Album podem ser acessados com o método photos. Por exemplo, considerando que @album é um objeto da classe Album com fotos anexadas, podemos apresentar as imagens na view com image_tag utilizando:

<h1><%= @album.title %></h1>

<% @album.photos.each do |photo| %>
  <%= image_tag photo %>
<% end %>

Se quisermos criar agora uma view única para visualizar cada foto, podemos utilizar as rotas aninhadas do Rails (nested resources). Assim, cada foto pode ter uma rota hierarquicamente "dentro" da rota do álbum ao qual pertence. Para isso, no arquivo de rotas, podemos incluir:

resources :albums do
  resources :photos, only: [:show]
end

Isso deve gerar rotas como /albums/:album_id/photos/:id. Dessa forma podemos incluir um link para cada foto com a rota que desejamos:

<h1><%= @album.title %></h1>

<% @album.photos.each do |photo| %>
  <%= link_to(image_tag photo), album_photo_path(@album, photo) %>
<% end %>

Assim, cada imagem ganha um link e, ao ser clicada, somos redirecionados a view separada da foto. Agora, para tornar acessível cada imagem em sua view específica, precisamos fazer uma query no PhotosController. Como não existe um model Photos, somente uma relação no ActiveStorage, não é possível fazer uma query pelo id do anexo, mas podemos executar:

class PhotosController < ApplicationController

  def show
    @photo = Album.find(params[:album_id]).photos.find(params[:id]) 
  end
end

Aproveitamos esse exemplo bastante simples para demonstrar o funcionamento básico do ActiveStorage para envio de múltiplos arquivos e as nested resources do Rails para criação de rotas aninhadas para cada arquivo. Por essa razão, a query acima parece um pouco estranha e provavelmente uma aplicação real apresentará uma solução diferente para isso.

O ActiveStorage é uma ferramenta fácil de usar, que já faz parte do framework Rails (a partir da versão 5.2) e pode substituir outras ferramentas externas como o Paperclip (descontinuada) ou o CarrierWave.

Referências

Foto de perfil do autor
André Kanamura

Dev na Campus Code