Atualmente estou trabalhando em uma aplicação Rails que faz inúmeros acessos à imagens guardadas em um banco de dados. Acontece que as imagens são ENORMES! E na maioria das vezes (pra não dizer todas) não há a necessidade de apresentar para o usuário a imagem em alta resolução.

Então pensei em thumbnails. Aquelas imagens pequenininha, que abrem rapidamente, e que geralmente quando clicamos sobre elas, abrimos a imagem original em alta resolução.

Bom, mas como fazer isso em Rails?

Recorri ao bom e velho Google e encontrei algumas soluções.

Ruby possui algums gems que auxiliam na manipulação de imagens utilizando ImageMagick, dentre eles o rmagick, image_science e o minimagick. Durante a pesquisa, descobri também alguns plugins para rails que facilitam o upload de arquivos binários, file_column, acts_as_attachment e atachment_fu.

Depois de ler algo sobre todos eles, resolvi usar, minimagick + atachment_fu. Como esperado, a junção dos dois mostrou-se extremamente eficiente, tornando meu trabalho mais simples e menos demorado.

Então, agora, nesse artigo, mostrarei como utiliza-los para fazer upload de imagens e guarda-las no banco de dados em versão thumbnails e original.

Obs: Todos os passos seguidos nesse tutorial foram realizados usando Rails 2.0.2 em um Ubuntu 7.10

Primeiramente, precisamos instalar o ImageMagick, que é o responsável por transformar usas imagens em thumbnails.

No caso do ubuntu é bem fácil!

sudo apt-get install imagemagick

Agora se você usa outra distribuição linux, windows ou mac, é só entrar no site oficial do ImageMagick e fazer o download da versão apropriada.

Depois temos que instalar o gem minimagick.

sudo gem install mini_magick

e criar nossa aplicação rails

rails thumbnails

Como utilizaremos o plugin atachment_fu, precisamos baixá-lo e adicioná-lo ao projeto.

ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu/

Não se esqueça de configurar seu banco de dados (config/database.yml).

Pronto! Agora efetivamente podemos começar.

Como usaremos o attachment_fu, devemos criar 2 modelos, um (db_file) que será o responsável por armazenar no banco de dados as imagens (sejam elas em tamanho original ou thumbnails) e outro (image) para descrevê-las.

Par isso, vamos executar os seguintes comando

ruby script/generate model db_file

ruby script/generate scaffold image name:string description:text db_file_id:integer filename:string content_type:string size:integer width:integer height:integer parent_id:integer thumbnail:string created_at:datetime

O comando scaffold é usado para facilitar nosso trabalho, pois ele criará praticamente toda a estrutura da nossa aplicação.

Agora vamos editar os seguintes arquivos

Arquivo da migração da tabela db_files (001_create_db_files.rb)

class CreateDbFiles < ActiveRecord::Migration
def self.up
create_table :db_files do |t|
t.column :data, :longblob
end
end

def self.down
drop_table :db_files
end
end

Note que essa tabela tem um campo longblob onde serão armazenados os arquivos, e que seu nome (db_files) é padrão do plugin attachment_fu.

Arquivo da migração da tabela images (002_create_images.rb)

class CreateImages < ActiveRecord::Migration
def self.up
create_table :images do |t|
t.string :name
t.text :description
t.integer :db_file_id
t.string :filename
t.string :content_type
t.integer :size
t.integer :width
t.integer :height
t.integer :parent_id
t.string :thumbnail
t.datetime :created_at

t.timestamps
end
end

def self.down
drop_table :images
end
end

Esse arquivo foi totalmente gerado pelo scaffold e está pronto para ser usado. Note que exceto os campo name e description, todos os outros são de uso do plugin attachment_fu.

Obs: Adicionei os campos name e description somente para ilustrar que podemos adicionar outros campos, e não somentes os de uso do plugin.

Modelo Image (image.rb)

class Image < ActiveRecord::Base
has_attachment :storage => :db_file,
:max_size => 5.megabytes,
:thumbnails => { :thumb => ‘80×80>’},
:processor => :MiniMagick

validates_as_attachment
end

Aqui descrevemos que o modelo terá um anexo, e que geraremos um thumbnail chamado thumb de no máximo 80×80 utilizando o processor MiniMagick. Definimos também que o tamanho máximo do arquivo será de 5MB e que ele será armazenado na tabela db_file.

Obs: :storage => :db_file não é necessário, pois é padrão do attachment_fu

View index do controlador Images (index.html.erb)

<h1>Listing images</h1>

<table>
<tr>
<th>Image</th>
</tr>

<% for image in @images %>
<tr>
<td><%= image_tag h url_for(:action => “showImage”, :id =>image)%></td>
<td><%= link_to ‘Show’, image.parent %></td>
<td><%= link_to ‘Destroy’, image, :confirm => ‘Are you sure?’, :method => :delete %></td>
</tr>
<% end %>
</table>

<br />

<%= link_to ‘New image’, new_image_path %>

Retiramos todos os campos que não são interessantes, e deixamos somente as ações de mostrar e de destruir uma imagem armazenada.

Note:

  • que a imagem será formada na action showImage do controlador Images (veremos isso mais para frente);
  • e que a action show tem id=>image.parent (isso ocorre porque apresentaremos aqui somente os thumbnails, e quando o usuário clicar em show, a imagem original, ou seja, a parent, deverá ser mostrada)

View new do controlador Images (new.html.erb)

<h1>New image</h1>

<%= error_messages_for :image %>

<% form_for(@image, :url => {:action=>”create”}, :html => { :multipart => true }) do |f| %>
<p>
<b>Name</b><br />
<%= f.text_field :name %>
</p>

<p>
<b>Description</b><br />
<%= f.text_area :description %>
</p>

<p>
<b><label for=”uploaded_data”>Image:</label></b><br />
<%= f.file_field :uploaded_data %>
</p>

<p>
<%= f.submit “Create” %>
</p>
<% end %>

<%= link_to ‘Back’, images_path %>

Retire todos os campos, exceto o name e o description, e adicione o campo file_field para que seja possível fazer upload de arquivos. Todos os outros campos serão preenchidos pelo plugin attachment_fu automaticamente. Note que o form deve permitir :multipart => true.

View show do controlador Images (show.html.erb)

<p>
<b>Name:</b>
<%=h @image.name %>
</p>

<p>
<b>Description:</b>
<%=h @image.description %>
</p>

<p>
<%= image_tag h url_for(:action => “showImage”, :id =>params[:id])%>
</p>

<p>
<b>Created at:</b>
<%=h @image.created_at %>
</p>

|
<%= link_to ‘Back’, images_path %>

Novamente retiramos os campos que não interessam.

Controlador Images (images_controller.rb)

class ImagesController < ApplicationController

def index
@images = Image.find(:all,:conditions=>”thumbnail=’thumb’”)

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @images }
end
end
def show
@image = Image.find(params[:id])

respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @image }
end
end
def new
@image = Image.new

respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @image }
end
end

def create
@image = Image.new(params[:image])

respond_to do |format|
if @image.save
flash[:notice] = ‘Image was successfully created.’
format.html { redirect_to(@image) }
format.xml { render :xml => @image, :status => :created, :location => @image }
else
format.html { render :action => “new” }
format.xml { render :xml => @image.errors, :status => :unprocessable_entity }
end
end
end

def destroy
@image = Image.find(params[:id])
@thumb = Image.find(@image.parent)
@image.destroy
@thumb.destroy

respond_to do |format|
format.html { redirect_to(images_url) }
format.xml { head :ok }
end
end

def showImage
image = Image.find(params[:id])
send_data(image.db_file.data,:type=>image.content_type,:disposition=>”inline”)
end
end

As observações mais importantes que devem ser feitas nesse controlador são:

  • na action index temos @images = Image.find(:all,:conditions=>”thumbnail=’thumb’”), ou seja, estamos selecionando somente as imagens thumb (que definimos como no máximo 80×80)
  • na action destroy, destruímos 2 imagens: a original e a thumbnail

Agora execute

rake db:migrate

ruby script/server

e teste sua aplicação

http://localhost:3000/images/

Se você quiser fazer download dos arquivos desse tutorial, clique aqui.