Carga de Imagen en Rails: Usando Paperclip en una Aplicación Rails
Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)
En las primeras dos partes de esta serie, te mostré como habilitar la carga de imágenes en Rails usando CarrierWave. En esta parte, te mostraré cómo hacerlo usando Paperclip.
Paperclip es un gem de Ruby proporcionado por thoughtbot. Fue creado para hacer el adjuntar archivos muy fácil. En este tutorial, verás cómo usar Paperclip junto con Devise.
Sin mucha charla, pongamos manos a la obra.
Paperclip requiere la instalación de ImageMagick en tu máquina. Necesitas esto para procesamiento de imagen. Para instalar ImageMagick, usar cualquier de los pasos de abajo, dependiendo del tipo de máquina que uses.
Usuarios Mac:
brew install imagemagick
Usuarios Ubuntu:
sudo apt-get install imagemagick
Generación de Aplicación Rails
Usa tu terminal para generar una nueva aplicación.
rails new paperclip
Abre tu Gemfile y agrega los gems necesarios:
gem 'paperclip'
gem 'devise'
Ejecuta bundle install cuando termines.
Configuración Devise
Desde tu termina, instala devise usando el comando de abajo:
rails generate devise:install
Cuando eso esté terminado, ahora puedes generar tu modelo Usuario:
rails generate devise User
Migra tu base de datos después.
rake db:migrate
Genera tus vistas devise.
rails generate devise:views
Usando tu editor de texto, navega a app/views/layouts/application.html.erb y agrega el siguiente código justo arriba de bloque yield.
1 |
#app/views/layouts/application.html.erb |
2 |
|
3 |
<p class="notice"><%= notice %></p> |
4 |
<p class="alert"><%= alert %></p> |
Integración Paperclip
Debido a razones de seguridad, tenemos que permitir parámetros en el controlador Devise. Gracias al increíble equipo detrás de Devise, hacer esto es sencillo.
Abre app/controllers/application_controller.rb y pega las siguientes líneas de código.
1 |
#app/controllers/application_controller.rb |
2 |
|
3 |
class ApplicationController < ActionController::Base |
4 |
# Prevent CSRF attacks by raising an exception. |
5 |
# For APIs, you may want to use :null_session instead. |
6 |
|
7 |
protect_from_forgery with: :exception |
8 |
|
9 |
before_action :configure_permitted_parameters, if: :devise_controller? |
10 |
|
11 |
protected
|
12 |
|
13 |
def configure_permitted_parameters |
14 |
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me, :avatar, :avatar_cache) } |
15 |
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :password, :password_confirmation, :current_password, :avatar) } |
16 |
end
|
17 |
end
|
18 |
Abre tu modelo User y hazlo lucir como esto:
1 |
#app/models/user.rb |
2 |
|
3 |
class User < ActiveRecord::Base |
4 |
# Include default devise modules. Others available are: |
5 |
# :confirmable, :lockable, :timeoutable and :omniauthable |
6 |
devise :database_authenticatable, :registerable, |
7 |
:recoverable, :rememberable, :trackable, :validatable |
8 |
|
9 |
has_attached_file :avatar, styles: { medium: "300x300", thumb: "100x100" } |
10 |
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/ |
11 |
end
|
Necesitas agregar una columna avatar para tu tabla Users. Ahí hay un comando rails que hace esto posible desde tu terminal.
rails generate migration add_avatar_to_users
Eso creará una nueva migración en db/migrate. Ábrela y pega el siguiente código:
1 |
class AddAvatarToUsers < ActiveRecord::Migration |
2 |
def up |
3 |
add_attachment :users, :avatar |
4 |
end
|
5 |
|
6 |
def down |
7 |
remove_attachment :users, :avatar |
8 |
end
|
9 |
end
|
Ejecuta tu migración
rake db:migrate
Agrega Avatar a Formularios Devise
Editarás tu nuevo formulario de registro app/views/devise/registrations/new.html.erb y edita el formulario app/views/devise/registrations/edit.html.erb a lo que tengo abajo:
1 |
#app/views/devise/registrations/new.html.erb |
2 |
|
3 |
<h2>Sign up</h2> |
4 |
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { multipart: true }) do |f| %> |
5 |
<%= devise_error_messages! %> |
6 |
<div class="field"> |
7 |
<%= f.label :email %><br /> |
8 |
<%= f.email_field :email, autofocus: true %> |
9 |
</div>
|
10 |
|
11 |
<div class="field"> |
12 |
<%= f.label :password %> |
13 |
<% if @minimum_password_length %> |
14 |
<em>(<%= @minimum_password_length %> characters minimum)</em> |
15 |
<% end %><br /> |
16 |
<%= f.password_field :password, autocomplete: "off" %> |
17 |
</div>
|
18 |
|
19 |
<div class="field"> |
20 |
<%= f.label :password_confirmation %><br /> |
21 |
<%= f.password_field :password_confirmation, autocomplete: "off" %> |
22 |
</div>
|
23 |
|
24 |
<div class="field"> |
25 |
<%= f.file_field :avatar %> |
26 |
</div>
|
27 |
|
28 |
<div class="actions"> |
29 |
<%= f.submit "Sign up" %> |
30 |
</div>
|
31 |
<% end %> |
32 |
|
33 |
<%= render "devise/shared/links" %> |
1 |
#app/views/devise/registrations/edit.html.erb |
2 |
|
3 |
<h2>Edit <%= resource_name.to_s.humanize %></h2> |
4 |
|
5 |
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> |
6 |
<%= devise_error_messages! %> |
7 |
|
8 |
<div class="field"> |
9 |
<%= f.label :email %><br /> |
10 |
<%= f.email_field :email, autofocus: true %> |
11 |
</div>
|
12 |
|
13 |
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> |
14 |
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> |
15 |
<% end %> |
16 |
|
17 |
<div class="field"> |
18 |
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> |
19 |
<%= f.password_field :password, autocomplete: "off" %> |
20 |
</div>
|
21 |
|
22 |
<div class="field"> |
23 |
<%= f.label :password_confirmation %><br /> |
24 |
<%= f.password_field :password_confirmation, autocomplete: "off" %> |
25 |
</div>
|
26 |
|
27 |
<div class="field"> |
28 |
<%= f.file_field :avatar %> |
29 |
</div>
|
30 |
|
31 |
<div class="field"> |
32 |
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> |
33 |
<%= f.password_field :current_password, autocomplete: "off" %> |
34 |
</div>
|
35 |
|
36 |
<div class="actions"> |
37 |
<%= f.submit "Update" %> |
38 |
</div>
|
39 |
<% end %> |
40 |
|
41 |
<h3>Cancel my account</h3> |
42 |
|
43 |
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p> |
44 |
|
45 |
<%= link_to "Back", :back %> |
Inicia tu navegador y revisa lo que que tienes.
Para una aplicación estándar, podrías querer revisar si un usuario que quiere editar su perfil ya tiene un avatar cargado. Esto es fácil de implementar en tu archivo de edición de registro.
Abre el archivo de registro y hazlo lucir como esto:
1 |
#app/views/devise/registrations/edit.html.erb |
2 |
|
3 |
<h2>Edit <%= resource_name.to_s.humanize %></h2> |
4 |
|
5 |
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> |
6 |
<%= devise_error_messages! %> |
7 |
|
8 |
<div class="field"> |
9 |
<%= f.label :email %><br /> |
10 |
<%= f.email_field :email, autofocus: true %> |
11 |
</div>
|
12 |
|
13 |
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> |
14 |
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> |
15 |
<% end %> |
16 |
|
17 |
<div class="field"> |
18 |
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> |
19 |
<%= f.password_field :password, autocomplete: "off" %> |
20 |
</div>
|
21 |
|
22 |
<div class="field"> |
23 |
<%= f.label :password_confirmation %><br /> |
24 |
<%= f.password_field :password_confirmation, autocomplete: "off" %> |
25 |
</div>
|
26 |
|
27 |
<div class="field"> |
28 |
<%= f.file_field :avatar %> |
29 |
|
30 |
<% if @user.avatar? %> |
31 |
<%= image_tag @user.avatar.url(:thumb) %> |
32 |
<% end %> |
33 |
</div>
|
34 |
|
35 |
<div class="field"> |
36 |
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> |
37 |
<%= f.password_field :current_password, autocomplete: "off" %> |
38 |
</div>
|
39 |
|
40 |
<div class="actions"> |
41 |
<%= f.submit "Update" %> |
42 |
</div>
|
43 |
<% end %> |
44 |
|
45 |
<h3>Cancel my account</h3> |
46 |
|
47 |
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p> |
48 |
|
49 |
<%= link_to "Back", :back %> |
¿Puedes ver que cambió?
En el código de arriba, hay una declaración condicional para revisar si un avatar ya existe para un usuario usando la línea <% if @user.avatar? %>. Si esto devuelve true, la siguiente línea se ejecuta, de otro modo no.
Validaciones de Seguridad
La validación siempre es importante cuando se habilitan características de carga en tu aplicación web. Paperclip viene con medidas para asegurar tu aplicación.
Puedes usar cualquiera de las validaciones debajo en tu modelo.
1 |
class User < ActiveRecord::Base |
2 |
has_attached_file :avatar |
3 |
# Validate content type |
4 |
validates_attachment_content_type :avatar, content_type: /\Aimage/ |
5 |
# Validate filename |
6 |
validates_attachment_file_name :avatar, matches: [/png\Z/, /jpe?g\Z/] |
7 |
# Explicitly do not validate |
8 |
do_not_validate_attachment_file_type :avatar |
9 |
end
|
Conclusión
Podrías querer considerar Paperclip mientras construyes tu siguiente aplicación web. Tiene un gran equipo soportándolo.
Para explorar otras características no cubiertas en este tutorial, revisa la página GitHub de Paperclip.



