Advertisement
  1. Code
  2. Ruby on Rails

Subiendo Archivos Con Rails y Dragonfly

by
Difficulty:IntermediateLength:LongLanguages:

Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)

Hace algún tiempo escribí un artículo Subiendo Archivos con Rails y Shrine que explicó cómo introducir una característica de carga de archivos en tu aplicación Rails con la ayuda de la gem Shrine. Hay, sin embargo, muchas soluciones similares disponibles, y una de mis favoritas es Dragonfly---una solución de carga fácil de usar para Rails y Rack creada por Mark Evans.

Cubrimos esta librería a principios del año pasado pero, como con la mayoría del software, ayuda echar un vistazo a las librerías de vez en cuando para ver qué ha cambiado y cómo podemos emplearlas en nuestra aplicación.

En este artículo te guiaré a través de la configuración de Dragonfly y explicaré cómo utilizar sus características principales. Aprenderás como:

  • Integrar Dragonfly en tu aplicación
  • Configurar modelos para trabajar con Dragonfly
  • Introducir un mecanismo de subida básico
  • Introduce validaciones
  • Genera miniaturas de imagen
  • Realiza procesamiento de archivo
  • Almacena metadatos para archivos subidos
  • Prepara una aplicación para despliegue

Para hacer las cosas más interesantes, vamos a crear una pequeña aplicación musical. Esta presentará álbumes y canciones asociadas que pueden ser administradas y reproducidas en el sitio web.

El código fuente para este artículo está disponible en GitHub. También puedes revisar el demo funcionando de la aplicación.

Escuchando y Administrando Álbumes

Para comenzar, crea una nueva aplicación Rails sin la suite de prueba por defecto:

Para este artículo estaré usando Rails 5, pero la mayoría de los conceptos descritos aplican a versiones antiguas también.

Creando el Modelo, Controlador y Rutas

Nuestro sitio musical va a contener dos modelos, Album y Song. Por ahora, creemos el primero con los siguientes campos:

  • title (string)---contiene el título del álbum.
  • singer (string)---intérprete del álbum
  • image_uid (string)---un campo especial para almacenar la imagen previa del álbum. Este campo puede ser nombrado como cualquier cosa que quieras, pero debe contener el sufijo _uid como se instruye en la documentación Dragonfly.

Crea y aplica la migración correspondiente:

Ahora creemos un controlador muy genérico para administrar álbumes con todas las acciones por defecto:

albums_controller.rb

Por último, agrega las rutas:

config/routes.rb

Integrando Dragonfly

Es tiempo para que Dragonfly haga su aparición. Primero, agrega la gem al Gemfile:

Gemfile

Ejecuta:

El último comando creará un inicializador llamado dragonfly.rb con la configuración por defecto. Lo dejaremos de lado por ahora, pero podrías querer sobre las varias opciones en el sitio web oficial de Dragonfly.

La siguiente cosa importante es equipar nuestro modelo con los métodos de Dragonfly. Esto es hecho usando el dragonfly_accessor:

models/album.rb

Nota que aquí estoy diciendo :image---se relaciona directamente a la columna image_uid que creamos en la sección anterior. Si tu, por ejemplo, nombraste a tu columna photo_uid, entonces el método dragonfly_accessor necesitaría recibir :photo como un argumento.

Si estás usando Rails 4 o 5, otro paso importante es marcar el campo :image (¡no :image_uid!) como permitido en el controlador:

albums_controller.rb

Esto es prácticamente todo---¡estamos listos para crear vistas y comenzar a subir archivos!

Creando Vistas

Comienza con la vista index:

views/albums/index.html.erb

Ahora el parcial:

views/albums/_album.html.erb

Hay dos métodos Dragonfly para notar aquí:

  • album.image.url devuelve la ruta a la imagen.
  • album.image_stored? dice si el registro tiene un archivo subido en lugar.

Ahora agrega las páginas new y edit:

views/albums/new.html.erb

views/albums/edit.html.erb

views/albums/_form.html.erb

El formulario no es nada elegante, pero de nuevo nota que estamos diciendo :image, no :image_uid, cuando generamos la entrada de archivo.

¡Ahora puedes reiniciar el servidor y probar la característica de subida!

Quitando Imágenes

Así que los usuarios pueden crear y editar álbumes, pero hay un problema: ellos no tienen manera de quitar una imagen, solo reemplazarla con otra. Afortunadamente, esto es muy sencillo de arreglar introduciendo una casilla "remover imagen":

views/albums/_form.html.erb

Si el álbum tiene una imagen asociada, la mostramos y generamos una casilla. Si la casilla está marcada, la imagen será removida. Nota que si tu campo es nombrado photo_uid, entonces el método correspondiente para remover adjunto será remove_photo. Simple, ¿no es así?

La única otra cosa por hacer es permitir el atributo remove_image en tu controlador.

albums_controller.rb

Agregando Validaciones

En esta etapa, todo está funcionando bien, pero no estamos revisando la entrada del usuario en absoluto, lo que no es particularmente grandioso. Así pues, agreguemos validaciones para el modelo Álbum:

models/album.rb

validates_property es el método Dragonfly que revisaría varios aspectos de tu ajunto: podrías validar una extensión de archivo, tipo de MIME, tamaño, etc.

Ahora creemos un parcial genérico para mostrar los errores que fueron encontrados:

views/shared/_errors.html.erb

Emplea este parcial dentro del formulario:

views/albums/_form.html.erb

Estiliza los campos con errores un poco para representarlos visualmente:

stylesheets/application.scss

Reteniendo una Imagen Entre Peticiones

Habiendo introducido validaciones, nos encontramos con otro problema (todo un escenario típico, ¿no?): si el usuario ha cometido errores mientras completa el formulario, el o ella necesitará elegir el archivo de nuevo después de dar clic al botón Enviar.

Dragonfly puede ayudarte a resolver este problema así como usar un campo oculto retained_*:

views/albums/_form.html.erb

No olvides permitir este campo también:

albums_controller.rb

¡Ahora la imagen será persistente entre peticiones! El único problema pequeño, sin embargo, es que la entrada de carga de archivo aún mostrará el mensaje "elige un archivo", pero esto puede ser arreglado con algún estilo y un poco de JavaScript.

Procesando Imágenes

Generando Miniaturas

Las imágenes subidas por nuestros usuarios pueden tener dimensiones muy diferentes, lo que puede (y probablemente será) causar un impacto negativo en el diseño del sitio web. Probablemente te gustaría escalar las imágenes a alguna dimensión fija, y por supuesto esto es posible utilizando los estilos width y height. Esto es, sin embargo, una aproximación no óptima: el navegador aún necesitará descargar imágenes de tamaño completo y encogerlas.

Otra opción  (que usualmente es mucho mejor) es generar miniaturas de imagen con algunas dimensiones predefinidas en el servidor. Esto es realmente simple de lograr con Dragonfly:

views/albums/_album.html.erb

250x250 son, por supuesto, las dimensiones, en donde # es la geometría que significa "redimensiona y recorta si es necesario para mantener la relación de aspecto con gravedad central". Podrías encontrar información sobre otras geometrías en el sitio web de Dragonfly.

El método thumb es soportado por ImageMagick----una gran solución para crear y manipular imágenes. De ahí, para poder ver el demo funcionando de manera local, necesitarás instalar ImageMagick (todas las plataformas principales están soportadas).

El soporte para ImageMagick está habilitado por defecto dentro del inicializador de Dragonfly:

config/initializers/dragonfly.rb

Ahora las miniaturas están siendo generadas, pero no se almacenan en ningún lugar. Esto significa que cada vez que un usuario visite la página de álbumes, las miniaturas serán generadas. Hay dos maneras de superar este problema: generándolas después de que el registro es guardado o realizando la generación al vuelo.

La primera opción involucra introducir una nueva columna para almacenar la miniatura y modificar el método dragonfly_accessor. Crea y aplica una nueva migración:

Ahora modifica el modelo:

models/album.rb

Nota que ahora la primera llamada a dragonfly_accessor envía un bloque que realmente genera la miniatura por nosotros y la copia en la image_thumb. Ahora solo usa el método image_thumb en tus vistas:

views/albums/_album.html.erb

Esta solución es la más simple, pero no es recomendada por la documentación oficial y, lo que es peor, al momento de escribir no funciona con los campos retained_*.

Así pues, déjame mostrarte otra opción: generar miniaturas al vuelo. Esto involucra crear un nuevo modelo y modificar el archivo de configuración de Dragonfly. Primero, el modelo:

La tabla thumbs hospedará tus miniaturas, pero serán generadas bajo demanda. Para hacer que esto suceda, necesitamos redefinir el método url dentro del inicializador Dragonfly:

config/initializers/dragonfly.rb

Ahora agrega un nuevo álbum y visita la página raíz. La primera vez que lo haces, la siguiente salida será impresa en los registros:

Esto significa efectivamente que la miniatura está siendo generada por nosotros por ImageMagick. Si recargas la página, sin embargo, esta línea no aparecerá más, ¡significando que la miniatura fue cacheada! Podrías leer un poco más sobre esta característica en el sitio web de Dragonfly.

Más Procesamiento

Podrías realizar virtualmente cualquier manipulación a tus imágenes después de que fueron cargadas. Esto puede hacerse dentro del callback after_assign. Convirtamos, por ejemplo, todas nuestras imágenes a formato JPEG con 90% de calidad:

Hay más acciones que puedes realizar: rotar y cortar las imágenes, codificar con un formato diferente, escribir texto en ellas, mezclar con otras imágenes (por ejemplo, colocar una marca de agua), etc. Para ver algunos otros ejemplos, refiérete a la sección ImageMagick en el sitio web de Dragonfly.

Subiendo y Administrando Canciones

Por supuesto, la parte principal de nuestro sitio musical son canciones, así que vamos a agregarlas ahora. Cada canción tiene un título y un archivo musical, y pertenece a un álbum:

Engancha los métodos Dragonfly, como hicimos con el modelo Album:

models/song.rb

No olvides establecer una relación has_many:

models/album.rb

Agrega nuevas rutas. Una canción siempre existe en el alcance de un álbum, así que haré estas rutas anidadas:

config/routes.rb

Crea un controlador muy simple (una vez más, no olvides permitir el campo track):

songs_controller.rb

Muestra las canciones y un enlace para agregar una nueva:

views/albums/show.html.erb

Codifica el formulario:

views/songs/new.html.erb

Por último, agrega el parcial _song:

views/songs/_song.html.erb

Aquí estoy usando la etiqueta HTML5 audio, que no funcionará para navegadores más antiguos. Así que, si estás apuntando a soportar dichos navegadores, usa un polyfill.

Como ves, todo el proceso es muy sencillo. A Dragonfly realmente no le importa qué tipo de archivo quieras subir; todo lo que necesitas hacer es proporcionar un método dragonfly_accessor, agregar un campo apropiado, permitirlo, y generar una etiqueta de archivo de entrada.

Almacenando Metadatos

Cuando abro una lista de reproducción, espero ver alguna información adicional sobre cada canción, somo su duración o bitrate. Por supuesto, por defecto esta información no es almacenada en ningún lugar, pero podemos arreglar eso fácilmente. Dragonfly nos permite proporcionar información adicional sobre cada archivo subido y recuperarla después usando el método meta.

Las cosas, sin embargo, son un poco más complejas cuando estamos trabajando con audio o video, porque para recuperar sus metadatos, se necesita un gem streamio-ffmpeg especial. Esta gem, en su momento, depende de FFmpeg, así que para poder proceder necesitarás instalarlo en tu PC.

Agrega streamio-ffmpeg al Gemfile:

Gemfile

Instálalo:

Ahora podemos emplear el mismo callback after_assign ya visto en las secciones anteriores:

models/song.rb

Nota que aquí estoy usando un método path, no url, porque en este punto vamos a trabajar con un archivo temporal. Después solo extraemos la duración de la canción (convirtiéndola a minutos y segundos con ceros a la izquierda) y su bitrate (convirtiéndolo a kilobytes por segundo).

Por último, muestra metadatos en la vista:

views/songs/_song.html.erb

Si revisas los contenidos de la carpeta public/system/dragonfly (la ubicación por defecto para hospedar las subidas), notarás algunos archivos .yml---están almacenando toda la información meta en formato YAML.

Desplegando a Heroku

El último tema que cubriremos hoy es cómo preparar tu aplicación antes de desplegarla a la plataforma en la nube de Heroku. El problema principal es que Heroku no te permite almacenar campos personalizados (como subidas), así que debemos depender de un servicio de almacenamiento en la nube como Amazon S3. Afortunadamente, Dragonfly puede ser integrado con este fácilmente.

Todo lo que necesitas hacer es registrar una nueva cuenta en AWS (si no la tienes ya), crea un usuario con permiso para acceder a cubetas S3, y escribir el par de llaves del usuario en una ubicación segura. Podrías usar un par de llaves raíz, pero esto realmente no es recomendado. Por último, crea una cubeta S3.

Regresando a nuestra aplicación Rails, agrega una nueva gem:

Gemfile

Instálalo:

Después modifica la configuración de Dragonfly para usar S3 en un entorno de producción:

config/initializers/dragonfly.rb

Para proporcionar variables ENV en Heroku, usa este comando:

Si quisieras probar la integración con S3 de manera local, podrías usar una gem como dotenv-rails para administrar variables de entorno. Recuerda, sin embargo, ¡que tu par de llaves AWS no debe ser expuesta de manera pública!

Otro pequeño asunto con el que me he encontrado mientras despliego a Heroku fue la ausencia de FFmpeg. La cosa es que cuando una nueva aplicación Heroku está siendo creada, tiene un conjunto de servicios que están siendo usado comúnmente (por ejemplo, ImageMagick está disponible por defecto). Otros servicios pueden ser instalado como complementos Heroku o en la forma de buildpacks. Para agregar un buildpack FFmpeg, ejecuta el siguiente comando:

Ahora todo está listo, ¡y puedes compartir tu aplicación musical con el mundo!

Conclusión

¿Esto fue un largo viaje, no es así? Hoy hemos discutido Dragonfly---una solución para subir archivos en Rails. Hemos visto su configuración básica, algunas opciones de configuración, generación de miniaturas, procesamiento y almacenamiento de metadatos. También, hemos integrado Dragonfly con el servicio Amazon S3 y preparado nuestra aplicación para despliegue en producción.

Por supuesto, no hemos discutido todos los aspectos de Dragonfly en este artículo, así que asegúrate de navegar a su sitio web oficial para encontrar documentación extensiva y ejemplos útiles. Si tienes alguna pregunta o te atoras con algún ejemplo de código, no dudes en contactarme.

Gracias por permanecer conmigo, ¡y nos vemos pronto!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.