1. Code
  2. Ruby
  3. Ruby on Rails

Carga de Imágenes en Rails Usando Dragonfly

Scroll to top

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.