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.