Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails

Enviando Arquivos Com Rails e Shrine

by
Difficulty:IntermediateLength:LongLanguages:

Portuguese (Português) translation by Erick Patrick (you can also view the original English article)

Há várias gems de envio de arquivos por aí, como CarrierWave, Paperclip e Dragonfly. Elas tem suas especificidades, e provavelmente já usou uma dessas gems.

Hoje, contudo, apresentaremos uma relativamente nova solução, mas legal, criada por Janko Marahnić: Shrine. Em contraste a algumas gems, ela tem uma abordagem modular, significando que cada recurso é um módulo (ou plugin, na terminologia de Shrine). Quer suportar validações? Adicionemos um plugin. Queremos processamento de arquivo? Plugin! Gostamos muito dessa abordagem, já que nos permite controlar facilmente que recursos estão disponível a qual modelo.

Nesse artigo mostraremos como:

  • integrar Shrine na aplicação Rails
  • configurá-lo (globalmente ou por modelo)
  • adicionar a possibilidade de enviar arquivos
  • processar arquivos
  • adicionar regras de validação
  • salvar metadados adicionais e empregar armazenamento na nuvem com Amazon S3

O código fonte desse artigo está disponível no GitHub.

Uma demo funcional pode ser vista aqui.

Integrando Shrine

Para começar, criemos uma nova aplicação Rails sem o conjunto de testes padrão:

Usaremos Rails 5, mas a maioria dos conceitos aplicam-se às versões 3 ou 4 também.

Coloquemos a gem de Shrine no Gemfile:

E executemos:

Agora, requeiramos um modelo que chamaremos de Photo. Shrine salva as informações relacionadas a arquivo numa coluna de texto, terminada em _data. Criemos e apliquemos a migração correspondente:

Notemos que para versões antigas do Rails, o comando anterior deve ser:

Configurações para Shrine podem ser configuradas globalmente e por modelo. As globais são feitas, claro, no arquivo inicializador. Lá, atrelaremos os arquivos necessários e plugins. Plugins são usados na Shrine para extrair funcionalidades em módulos separados, dando controle total de todos os recursos disponíveis. Por exemplo, há plugins para validação, processamento de imagem, salvar anexos em cache e mais.

Por hora, adicionemos dois plugins: um para ActiveRecord e outro para log. Eles serão inclusos globalmente. Também configuraremos o armazenamento de arquivos:

config/initializers/shrine.rb

O registrador retornará informações de depuração dentro do terminal para nós, dizendo quanto tempo foi gasto processando cada arquivo. Isso pode vir a calhar.

Todos os arquivos enviados serão salvos na pasta public/uploads. Não queremos registrá-los no Git, então excluamos a pasta:

.gitignore

Agora, criaremos uma classe "uploader" especial que guardará configurações específicas do modelo. Por hora, essa classe ficará vazia:

models/image_uploader.rb

Por último, incluamos essa classe no modelo Photo:

models.photo.rb

[:image] adiciona um atributo virtual que será usado ao construir o formulário. A linha acima pode ser reescrita como:

Legal! Agora, o modelo está equipado com a funcionalidade de Shrine e podemos continuar.

Controladores, Visões e Rotas

Para essa demo, precisaremos de apenas um controlador para administrar as fotos. A página index servirá como raiz:

pages_controller.rb

A visão:

views/photos/index.html.erb

Para renderizar o vetor @photos, uma parcial é necessária:

views/photos/_photo.html.erb

image_data? é um método de Shrine que verifica se um registro tem uma imagem.

image_url é outro método de Shrine que retorna um caminho para a imagem original. Claro, é muito melhor uma miniatura, mas lidaremos com isso depois.

Adicionemos todas as rotas necessárias:

config/routes.rb

É isso—a base está feita e podemos continuar com a parte interessante!

Enviando Arquivos

Nessa seção, mostraremos como adicionar a funcionalidade que enviará os arquivos. As ações do controlador são bem simples:

photos_controller.rb

O único problema é que para parâmetros fortes, temos de permitir o atributo virtual image, não image_data.

photos_controller.rb

Criemos a visão new:

views/photos/new.html.erb

A visão parcial do formulário também é trivial:

views/photos/_form.html.erb

De novo, notemos que usamos image não image_data.

Por último, adicionemos outra parcial para mostrar os erros:

views/shared/_errors.html.erb

Isso é tudo—podemos começar a enviar arquivos agora.

Validações

Claro, ainda é preciso muita coisa para completar o app demo. O principal problema é que usuários podem enviar qualquer coisa e de qualquer tamanho, que não é bem legal. Assim, adcionemos um plugin para validações:

config/inititalizers/shrine.rb

Configuremos a lógica da validação para ImageUploader:

models/image_uploader.rb

Permitimos apenas imagens JPG e PNG menores que 1MB. Ajustemos as regras como quisermos.

Tipos MIME

Algo importante a notar é que, por padrão, Shrine determinará o tipo MIME do arquivo usando o cabeçalho HTTP Content-Type. O cabeçalho é passado pelo navegador e configurado apenas pela extensão do arquivo, que não é desejável.

Se quisermos determinar o tipo MIME baseado no conteúdo do arquivo, usemos um plugin chamado determine_mime_type. Nós incluiremos dentro da classe uploader, já que outros modelos podem não precisar da funcionalidade:

models/image_uploader.rb

Esse plugin usará o utilitário file do Linux por padrão.

Salvando Imagens Anexas em Cache

Atualmente, quando um usuário enviar um formulário com dados incorretos, ele será mostrado com os erros acima. O problema, contudo, é que a imagem anexa é perdida e o usuário terá de selecioná-la novamente. É muito fácil de ajustar, com outro plugin chamado cached_attachment_data:

models/image_uploader.rb

Agora, adicionemos um campo escondido ao formulário.

views/photos/_form.html.erb

Editando uma Foto

Agora, as imagens podem ser enviadas, mas não é possível editá-las. Corrijamos isso. As ações correspondentes do controlador são triviais:

photos_controller.rb

A mesma parcial _form será usada:

views/photos/edit.html.erb

Legal, mas não o suficiente: usuários não podem remover uma imagem enviada. Para permitir isso, precisaremo de outro plugin:

models/image_uploader.rb

Ele usa um atributo virtual chamado :remove_image, então permitamo-no no controlador:

photos_controller.rb

Agora, mostremos a caixa de seleção para remover uma imagem se um registro já existir:

views/photos/_form.html.erb

Gerando uma Imagem Miniatura

Por hora, mostramos as imagens originais, que não é a melhor abordagem para pré-visualização: fotos podem ser grandes e ocupar muito espaço. Claro, podemos simplesmente usar atributos width e height do CSS, mas é uma má ideia. Mesmo que a imagem tenha estilos para ser pequena, o usuário ainda baixará o arquivo original que será muito grande.

Assim, é muto melhor criar uma imagem de pré-visualização pequena no lado do servidor durante envio inicial. Isso envolve dois plugins e duas gems adicionais. Primeiro, as gems:

Image_processing é uma gem criada pelo autor de Shrine. Ela possui alguns métodos de alto nível para manipular imagens. Essa gem, por sua vez, depende de mini_magick, um envólucro para ImageMagick. Como imaginado, precisamos de ImageMagick instalada no sistema para rodar a demo.

Instalemos essas gems:

Agora, incluamos os plugins junto de suas dependências:

models/image_uploader.rb

Processing é o plugin que manipulará a imagem (por exemplo, para reduzir, girar, converter, etc). Versions, por sua vez, permite-nos ter variações de imagens. Para a demo, duas versões serão salvas: "original" e "thumb" (com 300x300px).

Eis o código para processar uma imagem e salvar as versões:

models/image_uploader.rb

resize_to_limit! é provida pela gem image_processing. Ele redimensiona uma imagem para 300x300 se ela for maior e faz nada se for menor. Além disso, mantem as proporções.

Agora, ao mostrar a imagem, apenas precisamos prover o argumento :original ou :thumb para o método image_url:

views/photos/_photo.html.erb

O mesmo pode ser feito dentro do formulário:

views/photos/_form.html.erb

Para remover automaticamente os arquivos processados após envio finalizar, podemos usar o plugin delete_raw:

models/image_uploader.rb

Metadados da Imagem

Além de renderizar uma imagem, também podemos pegar seus metadados. Por exemplo, mostremos o tamanho original da foto e seu MIME:

views/photos/_photo.html.erb

E as dimensões? Infelizmente, não são salvos por padrão, mas é possível com um plugin chamado store_dimensions.

Dimensões da Imagem

O plugin store_dimensions depende da gem fastimage, então adicionemo-na:

Não esqueçamos de executar:

Agora, incluamos o plugin:

models/image_uploader.rb

E mostremos as dimensões usando os métodos width e height:

views/photos/_photo.html.erb

Também há o método, dimensions, que retorna um vetor com a largura e altura (por exemplo, [500, 750]);

Movendo para a Núvem

Desenvolvedores geralmente escolhem serviços da nuvem para salvar arquivos enviados e Shrine tem tal possibilidade. Nessa seção, mostraremos como enviar arquivos para Amazon S3.

O primeiro passo é incluir as gems ao Gemfile:

aws-sdk é necessário para trabalhar com a SDK da S3, e dotenv-rails será usada para lidar com variáveis de ambiente.

Antes de seguir, devemos obter um par de chaves de acesso via API à S3. Para isso, acesse o Console da Amazon Web Services e navegue até Credenciais de Segurança > Usuários. Criemos um usuário com permissões para manipular arquivos na S3. Eis uma regra simples com acesso total à S3:

Baixemos o par de chames criados. Alternativamente, podemos usar chaves de acesso root, mas desencorajamos já que é bem inseguro.

Depois, criemos um balde do S3 para guardar os arquivos e adicionemos um arquivo à raiz do projeto para a configuração:

.env

Nunca exponha esse arquivo ao público, então, excluamo-nos do Git:

.gitignore

Agora, modifiquemos a configuração global de Shrine e introduzamos o novo armazenamento:

config/initializers/shrine.rb

É isso! Nenhuma mudança nas outras partes do app e já podemos testar nosso novo armazenamento. Se recebermos erros da S3 por chaves incorretas, garantamos que nossas chaves foram copiadas corretamente, sem qualquer espaço ou símbolos invisíveis.

Conclusão

Chegamos ao fim do artigo. Esperamos que se sinta confiante em usar Shrine e queira usá-lo em um de seus projetos. Nós discutimos vários recursos dessa gem, mas há ainda mais, como a possibilidade de guardar um contexto adicional junto dos arquivo e o mecanismo de envio direto.

Assim, navegue pela documentação da Shrine e pelo seu site oficial, que descreve bem todos os plugins disponíveis. Se tiver quaisquer perguntas sobre a gem, não hesite em publicá-las. Agradecemos por lerem tudo e vemo-nos logo!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.