Upload e Validação de Imagens e Arquivos com a gem Paperclip e Rails

Fala pessoal, beleza?

Hoje estou aqui para mostrar como fazer upload e validação de imagens e arquivos no Ruby on Rails (versão 3.2.8) com a gem Paperclip.

Irei utilizar o projeto que criei no tutorial onde ensino como iniciar com Rails, ele só tem uma conexão com o banco de dados MySQL e a rota raiz definida.

Índice

Escopo

Vamos supor que temos um cadastro de carros para fazer que deve conter os seguintes campos:

  • nome: string
  • descrição: text
  • imagem: string

Gerando um CRUD de registros

Para começar vamos gerar as páginas responsáveis por fazer cadastro, edição, exclusão e exibição de nossos registros. Para isso, abra o terminal na pasta do projeto e digite o comando rails g scaffold Car name:string description:text image:string para gerar os arquivos e depois rake db:migrate para criar a tabela de carros:

criando-scaffold-com-gerador-rails

Se iniciarmos o servidor (rails s) e acessarmos o projeto na url http://0.0.0.0:3000/cars teremos a tela onde podemos manipular nossos registros.

controle-de-registros-gerado-com-rails

Instalando e configurando a gem Paperclip

Agora que temos nosso CRUD funcionando vamos implantar o upload da imagem dos carros.

Vamos dizer ao Rails para usar a gem Paperclip, abra o arquivo Gemfile na pasta do projeto e adicione gem "paperclip", "~> 3.0". Feito isso, rode o comando bundle install no terminal para obtermos a gem e suas dependências.

O Paperclip usa a biblioteca ImageMagick para manipular imagens, devemos garantir que ela esteja instalada e com acesso, para isso rode which convert no terminal, copie o resultado (você obterá algo como /usr/bin/convert, desconsidere a parte do convert) e adicione a seguinte linha no arquivo de configuração de ambiente:

Paperclip.options[:command_path] = "/usr/bin/"

No caso do ambiente de desenvolvimento, adicionamos ela no arquivo config/environments/development.rb.

Usando a gem Paperclip

Já temos a gem instalada, agora vamos abrir o model de carros (app/models/car.rb) e dizer que iremos fazer upload de arquivos no campo image através da linha abaixo:

has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "120x90#" }

Observer a chave styles, ela serve para definir os tamanhos de miniaturas que desejamos criar, nesse caso estou criando duas, uma com tamanho de 300x300 (medium) e outra com tamanho de 120x90 (thumb). O # na frente diz para cortar a imagem centralizada, mantendo assim a dimensão solicitada e > diz que a imagem só deve ser redimensionada se ela for maior do que a medida. Veja mais aqui.

Precisamos alterar o campo de imagem no formulário que está como input text para input file, abra o partial do formulário (app/views/cars/_form.html.erb) e deixe como abaixo.

<div class="field">
<%= f.label :image %><br />
<%= f.file_field :image %>
</div>

Não podemos esquecer de ativar o multipart/form-data no formulário, para isso adicione :html => { :multipart => true } na tag de criação do form, veja:

<%= form_for(@car, :html => { :multipart => true }) do |f| %>

Também temos que alterar o nome do campo no banco de dados de image para image_file_name pois esse é o padrão do Paperclip para armazenar o nome/caminho da imagem.

Faça um teste e veja que o upload já está funcionando..

listagem-de-registros-com-imagem

Só que algo está errado ai, não queremos exibir o caminho da imagem, e sim ela, para isso abra o arquivo de listagem de carros (app/views/cars/index.html.erb) e substitua a linha:

<td><%= car.image %></td>

por

<td><%= image_tag(car.image.url(:thumb)) %></td>

Agora estamos chamando o helper image_tag passando como parâmetro a url da imagem pequena (thumb).

Para exibir a de tamanho médio usariamos :medium ou :original para exibir a imagem no tamanho original.
listagem-de-registros-com-imagem-exibindo-miniaturas

Personalizando caminho de upload

Se você quer escolher onde as imagens serão salvas é possível através das chaves path e url, veja:

has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "120x90#" },
:path => ':rails_root/public/images/cars/:id-:basename-:style.:extension',
:url => '/images/cars/:id-:basename-:style.:extension'

Aqui digo para salvar as imagens de carros na pasta public/images/cars e defino a url para o mesmo caminho.

Adicionando validações

Com certeza não podemos deixar de fora as validações, vamos ver algumas..

Presença obrigatória

Adicione a chamada para o método validates_attachment passando o campo a ser validado e definindo a presença obrigatória.

validates_attachment :image,
:presence => true

Tipo de arquivo (content type)

Usando um array de content type:

validates_attachment :image,
:content_type => { :content_type => ['image/jpg', 'image/png'] }

Ou por expressão regular, permitindo qualquer tipo de imagem:

validates_attachment :image,
:presence => true,
:content_type => { :content_type => /image/ }

Para fazer essa validação precisamos adicionar attr_accessor :image_content_type em nosso model para que o atributo consiga ser lido e escrito (get e set).

Tamanho de arquivo (file size)

validates_attachment :image,
:presence => true,
:size => { :in => 0..10.kilobytes }

ou

validates_attachment :image,
:presence => true,
:size => { :in => 0..3.megabytes }

Assim como o de cima, precisamos adicionar attr_accessor :image_file_size em nosso model.

Dimensões de imagens

Uma validação que sem dúvida é muito importante também é a de dimensões de imagem, com ela podemos definir um tamanho mínimo da figura a ser feito upload.

validate :file_dimensions, :unless => 'errors.any?'

def file_dimensions
if image.size
dimensions = Paperclip::Geometry.from_file(image.queued_for_write[:original].path)
if dimensions.width < 120 || dimensions.height < 90
errors.add(:image,'deve ter no minimo 120px de largura por 90px de altura')
end
end
end

Bem simples né, criamos um método que verifica se tem imagem pra subir, pega as dimensões da imagem original e faz a verificação. Substitua 120 e 90 pela largura e altura mínima que você deseja respectivamente.

Por hoje é só pessoal, o que vocês acharam?

Abraços.

Written on October 20, 2012

Share: