Carga de Imágenes en Rails Usando Dragonfly
Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)
La carga de archivos es una característica importante en las aplicaciones web. Además de habilitar a los usuario para subir fotos de perfil, el uso de las características de carga de archivos varía. Te he mostrado cómo habilitar la carga de archivos en tu aplicación Rails usando diferentes gems. El día de hoy te estaré mostrando cómo hacer lo mismo usando Dragonfly.
Dragonfly es un gem de Ruby altamente personalizable para manejar imágenes y otros adjuntos y ya está en uso en miles de sitios web.
Podrías tener una tarea de habilitar la carga de archivos en una aplicación Rails y podrías no querer hacer uso de otros gems que están ahí afuera. Puedes darle a Dragonfly una oportunidad, y definitivamente no te arrepentirás.
En este tutorial crearás una aplicación Rails simple; nombré la mía Dragon-Uploader. La aplicación tendrá solo una característica: carga de imagen.
Instalando ImageMagick
Para usar dragonfly, necesitas Imagemagick instalado en tu máquina. Sigue cualquier de los pasos de abajo, dependiendo de tu sistema operativo.
Usuarios Mac:
brew install imagemagick
Usuarios Ubuntu:
sudo apt-get install imagemagick
Generación de Aplicación Rails
rails new dragon-uploader -T
La opción -T asegura que tu aplicación Rails es generada sin la suite de prueba por defecto.
Ve a tu Gemfile y agrega el gem dragonfly.
1 |
#Gemfile |
2 |
|
3 |
gem 'dragonfly', '~> 1.0', '>= 1.0.12' |
No olvides hacer bundle.
bundle install
Generemos nuestro controlador.
rails generate controller Photos
Integrando Dragonfly
El primer paso para integrar Dragonfly en tu aplicación rails es ejecutar el comando de generación dragonfly desde tu terminal.
rails generate dragonfly
Esto creará un archivo inicializador para Dragonfly en tu folder config/initializers.
El archivo lucirá así:
1 |
#config/intializers/dragonfly.rb |
2 |
|
3 |
require 'dragonfly' |
4 |
|
5 |
# Configure |
6 |
Dragonfly.app.configure do |
7 |
plugin :imagemagick |
8 |
|
9 |
secret "e83b8affbf1c807c7788c07d27e70e79fb0459f8e2c4375b59e60a3da11631e5" |
10 |
|
11 |
url_format "/media/:job/:name" |
12 |
|
13 |
datastore :file, |
14 |
root_path: Rails.root.join('public/system/dragonfly', Rails.env),
|
15 |
server_root: Rails.root.join('public')
|
16 |
end |
17 |
|
18 |
# Logger |
19 |
Dragonfly.logger = Rails.logger |
20 |
|
21 |
# Mount as middleware |
22 |
Rails.application.middleware.use Dragonfly::Middleware |
23 |
|
24 |
# Add model functionality |
25 |
if defined?(ActiveRecord::Base) |
26 |
ActiveRecord::Base.extend Dragonfly::Model |
27 |
ActiveRecord::Base.extend Dragonfly::Model::Validations |
28 |
end |
rails generate model Photo
1 |
#app/models/photo.rb |
2 |
|
3 |
class Photo < ActiveRecord::Base |
4 |
dragonfly_accessor :image |
5 |
end
|
Dragonfly proporciona un accesorio que necesitarás para agregar a tu modelo. con este puedes leer y escribir imágenes.
Ahora navega a tu archivo de migración y agrega columnas.
1 |
#xxx_create_photos.rb |
2 |
|
3 |
class CreatePhotos < ActiveRecord::Migration |
4 |
def change |
5 |
create_table :photos do |t| |
6 |
t.string :image_uid |
7 |
t.string :title |
8 |
|
9 |
t.timestamps null: false |
10 |
end
|
11 |
end
|
12 |
end
|
Nota: Si estás haciendo uso de avatar y no image como hice arriba, deberías cambiar la columna a avatar_uid.
Migra tu base de datos:
rake db:migrate
Configura tu PhotosController con las acciones necesarias para subir una imagen. Este debería lucir así:
1 |
#app/controllers/photos_controller.rb |
2 |
|
3 |
class PhotosController < ApplicationController |
4 |
def index |
5 |
@photos = Photo.all |
6 |
end
|
7 |
|
8 |
def new |
9 |
@photo = Photo.new |
10 |
end
|
11 |
|
12 |
def create |
13 |
@photo = Photo.new(photo_params) |
14 |
if @photo.save |
15 |
redirect_to photos_path |
16 |
else
|
17 |
render :new |
18 |
end
|
19 |
end
|
20 |
|
21 |
private
|
22 |
|
23 |
def photo_params |
24 |
params.require(:photo).permit(:image, :title) |
25 |
end
|
26 |
end
|
Necesitarás configurar tus rutas.
Por ahora, agrega rutas a las tres acciones que has creado.
1 |
#config/routes.rb |
2 |
|
3 |
Rails.application.routes.draw do |
4 |
resource :photos only: [:index, :new, :create] |
5 |
|
6 |
root to: "photos#index" |
7 |
end |
Necesitas configurar tus vistas como tengo abajo:
1 |
#app/views/photos/index.html.erb |
2 |
|
3 |
<h2>Photos</h2> |
4 |
|
5 |
<p id="notice"><%= notice %></p> |
6 |
|
7 |
<table>
|
8 |
<thead>
|
9 |
<tr>
|
10 |
<th>Title</th> |
11 |
<th>Image</th> |
12 |
<th colspan="3"></th> |
13 |
</tr>
|
14 |
</thead>
|
15 |
|
16 |
<tbody>
|
17 |
<% @photos.each do |photo| %> |
18 |
<tr>
|
19 |
<td><%= photo.title %></td> |
20 |
<td><%= link_to image_tag(photo.image.thumb('100x100').url), photo.image.url %></td> |
21 |
<td><%= link_to 'Show', photo %></td> |
22 |
<td><%= link_to 'Edit', edit_photo_path(photo) %></td> |
23 |
<td><%= link_to 'Destroy', photo, method: :delete, data: { confirm: 'Are you sure?' } %></td> |
24 |
</tr>
|
25 |
<% end %> |
26 |
</tbody>
|
27 |
</table>
|
1 |
#app/views/photos/new.html.erb |
2 |
|
3 |
<%= form_for @photo do |f| %> |
4 |
<div>
|
5 |
<%= f.label :title %> |
6 |
<%= f.text_field :title %> |
7 |
</div>
|
8 |
<div>
|
9 |
<%= f.label :image %> |
10 |
<%= f.file_field :image %> |
11 |
</div>
|
12 |
<div>
|
13 |
<%= f.submit :submit %> |
14 |
</div>
|
15 |
<% end %> |
Regresaremos a estas vistas después.
Validaciones
Por razones de seguridad, no quieres otorgar a tus usuarios el privilegio de subir archivos de cualquier tipo. Dragonfly te proporciona los métodos necesarios para esto en tus inicializadores.
1 |
#config/initializers/dragonfly.rb |
2 |
|
3 |
# Add model functionality |
4 |
if defined?(ActiveRecord::Base) |
5 |
ActiveRecord::Base.extend Dragonfly::Model |
6 |
ActiveRecord::Base.extend Dragonfly::Model::Validations |
7 |
end |
Ahora edita tu modelo de foto para que se vea como lo que tengo abajo:
1 |
#app/models/photo.rb |
2 |
|
3 |
class Photo < ActiveRecord::Base |
4 |
dragonfly_accessor :image |
5 |
|
6 |
#title validation |
7 |
validates_presence_of :title |
8 |
|
9 |
#image validations |
10 |
validates_presence_of :image |
11 |
validates_size_of :image, maximum: 400.kilobytes, |
12 |
message: "should not be more than 400KB", if: :image_changed? |
13 |
|
14 |
validates_property :format, of: :image, in: ['jpeg', 'png', 'gif'], |
15 |
message: "the formats allowed are: .jpeg, .png, .gif", if: :image_changed? |
16 |
end
|
Aquí está una lista completa de las validaciones que Dragonfly ofrece:
1 |
class Photo |
2 |
extend Dragonfly::Model::Validations |
3 |
|
4 |
validates_presence_of :image |
5 |
validates_size_of :image, maximum: 500.kilobytes |
6 |
|
7 |
# Check the file extension |
8 |
validates_property :ext, of: :image, as: 'jpg' |
9 |
# ..or.. |
10 |
validates_property :mime_type, of: :image, as: 'image/jpeg' |
11 |
# ..or actually analyse the format with imagemagick.. |
12 |
validates_property :format, of: :image, in: ['jpeg', 'png', 'gif'] |
13 |
|
14 |
validates_property :width, of: :image, in: (0..400), message: "é demais cara!" |
15 |
|
16 |
# ..or you might want to use image_changed? method.. |
17 |
validates_property :format, of: :image, as: 'png', if: :image_changed? |
18 |
end |
Puedes leer más sobre esto en la documentación Dragonfly.
También deberías considerar dar a tus usuarios la opción de editar sus imágenes guardadas. Para hacer esto, necesitamos agregar dos métodos de acción a nuestro PhotosController y crear una página de edición en nuestras vistas. Podrías querer agregar las acciones borrar y mostrar mientras estás en ello, como tengo abajo:
1 |
#app/controllers/photos_controller.rb |
2 |
|
3 |
class PhotosController < ApplicationController |
4 |
before_action :set_photos, only: [:show, :edit, :update, :destroy] |
5 |
|
6 |
def index |
7 |
@photos = Photo.all |
8 |
end
|
9 |
|
10 |
def new |
11 |
@photo = Photo.new |
12 |
end
|
13 |
|
14 |
def create |
15 |
@photo = Photo.new(photo_params) |
16 |
if @photo.save |
17 |
redirect_to @photo |
18 |
else
|
19 |
render :new |
20 |
end
|
21 |
end
|
22 |
|
23 |
def show |
24 |
end
|
25 |
|
26 |
def edit |
27 |
end
|
28 |
|
29 |
def update |
30 |
if @photo.update(photo_params) |
31 |
redirect_to @photo, notice: "photo successfully updated" |
32 |
else
|
33 |
render :edit |
34 |
end
|
35 |
end
|
36 |
|
37 |
def destroy |
38 |
@photo.destroy |
39 |
redirect_to photos_url, notice: 'photo was successfully destroyed.' |
40 |
end
|
41 |
|
42 |
private
|
43 |
|
44 |
def photo_params |
45 |
params.require(:photo).permit(:image, :title) |
46 |
end
|
47 |
|
48 |
def set_photos |
49 |
@photo = Photo.find(params[:id]) |
50 |
end
|
51 |
end
|
1 |
#app/views/photos/edit.html.erb |
2 |
|
3 |
<%= form_for @photo do |f| %> |
4 |
<% if @photo.errors.any? %> |
5 |
<div id="error_explanation"> |
6 |
<h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2> |
7 |
|
8 |
<ul>
|
9 |
<% @photo.errors.full_messages.each do |message| %> |
10 |
<li><%= message %></li> |
11 |
<% end %> |
12 |
</ul>
|
13 |
</div>
|
14 |
<% end %> |
15 |
<div>
|
16 |
<%= f.label :title %> |
17 |
<%= f.text_field :title %> |
18 |
</div>
|
19 |
<div>
|
20 |
<%= f.label :image %> |
21 |
<%= f.file_field :image %> |
22 |
</div>
|
23 |
<div>
|
24 |
<%= f.submit :submit %> |
25 |
</div>
|
26 |
<% end %> |
27 |
|
28 |
<%= link_to "Show", @photo %> | |
29 |
<%= link_to "Back", photos_path %> |
1 |
#app/views/photos/show.html.erb |
2 |
|
3 |
<div>
|
4 |
<strong>Title:</strong> |
5 |
<%= @photo.title %> |
6 |
</div>
|
7 |
<div>
|
8 |
<strong>Image:</strong> |
9 |
<%= image_tag @photo.image.thumb('400x200#').url if @photo.image_stored? %> |
10 |
</div>
|
11 |
|
12 |
<%= link_to 'Edit', edit_photo_path(@photo) %> | |
13 |
<%= link_to 'Back', photos_path %> |
Si intentas acceder a la página mostrar o editar, se te presentarán errores. Esto es porque restringimos la ruta a :new, :index, and :update. Ahora ve y cambia eso; debería lucir así:
1 |
#config/routes.rb |
2 |
|
3 |
Rails.application.routes.draw do |
4 |
resources :photos |
5 |
|
6 |
root to: "photos#index" |
7 |
end |
Conclusión
En este punto, ahora puedes integrar Dragonfly en tu aplicación Rails. Asegúrate de revisar la documentación si quieres probar más características no mencionadas aquí. Espero que lo hayas disfrutado.
Recuerda. siempre puedes agregar retroalimentación, preguntas, y comentarios en el formulario de abajo.



