Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails
Code

Subiendo Archivos Con Rails y Shrine

by
Difficulty:IntermediateLength:LongLanguages:

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

Hay muchas gemas ahí afuera para subir archivos como CarrierWave, Paperclip y Dragonfly, por nombrar algunos. Todos tienen sus particularidades, y probablemente has usado por lo menos una de estas gemas.

Hoy, sin embargo, quiero presentar una solución relativamente nueva pero muy interesante solución llamada Shrine, creada por Janko Marohnić. En contraste con algunas gemas similares, tiene una aproximación modular, significando que cada característica está empacada como un módulo (o complemento en terminología Shrine). ¿Quieres soportar validaciones? Agrega un complemento. ¿Quieres hacer algo de procesamiento de archivo? ¡Agrega un complemento! Realmente amo esta aproximación ya que te permite controlar fácilmente qué características estarán disponibles para cada modelo.

En este artículo te voy a mostrar cómo:

  • integrar Shrine a una aplicación Rails
  • configurarlo (globalmente y por modelo)
  • agregar la habilidad de subir archivos
  • procesar archivos
  • agregar reglas de validación
  • almacenar metadatos adicionales y emplear almacenamiento de archivo en la nube con Amazon S3

El código fuente para este artículo está disponible en GitHub.

El demo funcionando puede ser encontrado aquí.

Integrando Shrine

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

Estaré usando Rails 5 para este demo, pero la mayoría de los conceptos aplican a las versiones 3 y 4 también.

Deposita la gema Shrine en tu Gemfile:

Después ejecuta:

Ahora requeriremos un modelo que voy a llamar Photo. Shrine almacena toda la información relacionada al archivo en una columna de texto especial con un sufijo _data. Crea y aplica la migración correspondiente:

Nota que para versiones más antiguas de Rails, el siguiente comando debería ser:

Las opciones de configuración para Shrine pueden ser establecidas tanto globalmente como por modelo. Los ajustes globales están hechos, por supuesto, dentro del archivo inicializador. Ahí voy a enganchar los archivos y complementos necesarios. Los complementos son usados en Shrine para extraer piezas de funcionalidad en módulos separados, dándote control total de las características disponibles. Por ejemplo, hay complementos para validación, procesamiento de imagen, almacenamiento de adjuntos y más.

Por ahora, agreguemos dos complementos: uno para dar soporte a ActiveRecord y otro para configurar logging. Van a ser incluidos globalmente. También, configura el almacenamiento de archivos de sistema.

config/initializers/shrine.rb

Logger simplemente mostrará alguna información de depuración dentro de la consola por ti diciendo cuánto tiempo se invirtió en procesar un archivo. Esto puede ser útil.

Todos los archivos subidos serán almacenados dentro del directorio public/uploads. No quiero dar seguimiento a estos archivos en Git, así que excluye esta carpeta.

.gitignore

Ahora crea una clase "uploader" especial que va a hospedar ajustes específicos de modelo. Por ahora, esta clase va a estar vacía.

models/image_uploader.rb

Por último, incluye esta clase dentro del modelo Photo:

models/photo.rb

[:image] agrega un atributo virtual que será usando cuando se construya un formulario. La línea de arriba puede ser reescrita como:

¡Bien! Ahora el modelo está equipado con la funcionalidad de Shrine, y podemos proceder al siguiente paso.

Controlador, Vistas y Rutas

Para los propósitos de esta demostración, solo necesitaremos un controlador para administrar fotos. La página index servirá como la raíz:

pages_controller.rb

La vista:

views/photos/index.html.erb

Para poder general el arreglo @photos, se requiere un parcial:

views/photos/_photo.html.erb

image_data? es un método presentado por Shrine que revisa si un registro tiene una imagen.

image_url es otro método Shrine que simplemente regresa una ruta a la imagen original. Por supuesto, es mucho mejor mostrar una pequeña viñeta en su lugar, pero nos encargaremos de eso después.

Agrega todas las rutas necesarias:

config/routes.rb

Eso es todo--el trabajo preparatorio está hecho, ¡y podemos proceder a la parte interesante!

Subiendo Archivos

En esta sección te mostraré como puedes agregar la funcionalidad para realmente subir archivos. Las acciones de controlador son bastante simples:

photos_controller.rb

Lo único es que para los parámetros fuertes tienes que permitir el atributo virtual image, no image_data.

photos_controller.rb

Crea la vista new:

views/photos/new.html.erb

El parcial del formulario también es trivial:

views/photos/_form.html.erb

Una vez más, nota que estamos usando el atributo imagen, no el image_data.

Por último, agrega otro parcial para mostrar errores:

views/shared/_errors.html.erb

Esto es prácticamente todo--puedes comenzar subiendo imágenes justo ahora.

Validaciones

Por supuesto, mucho más trabajo tiene que ser hecho para completar la aplicación de demostración. El problema principal es que los usuarios podría subir absolutamente cualquier tipo de archivo con cualquier tamaño, lo cuál no es particularmente grandioso. Por lo tanto,m agrega otro complemento para soportar validaciones:

config/inititalizers/shrine.rb

Configura la lógica de validación para el ImageUploader:

models/image_uploader.rb

Solo estoy permitiendo imágenes JPG y PNG de menos de 1MB para ser subidas. Modifica estas reglas según creas conveniente.

Tipos MIME

Otra cosa importante que notar es que, por defecto, Shrine determinará el tipo MIME de un archivo usando el encabezado HTTP Content-Type. Este encabezado es pasado por el navegador y establecido solo basado en la extensión de archivo, lo cuál no siempre es deseable.

Si deseas determinar el tipo MME basado en los contenidos del archivo, entonces usa un complemento llamado determine_mime_type. Lo incluiré dentro de la clase uploader, mientras que otros modelos podrían no requerir esta funcionalidad:

models/image_uploader.rb

Este complemento va a usar la utilidad de archivo de Linux por defecto.

Almacenando Imágenes Adjuntas

Actualmente, cuando un usuario manda un formulario con datos incorrectos, el formulario se mostrará de nuevo con errores generados arriba. El problema, sin embargo, es que la imagen adjunta se perderá y el usuario necesitará seleccionarla de nuevo. Esto es muy sencillo de arreglar usando otro complemento llamado cached_attachment_data:

models/image_uploader.rb

Ahora simplemente agrega un campo oculto a tu formulario.

views/photos/_form.html.erb

Editando una Foto

Ahora las imágenes pueden ser subidas, pero no hay manera de editarlas, así que arreglemos eso de inmediato. Las acciones de controlador correspondientes son algo triviales:

photos_controller.rb

El mismo parcial _form sería utilizado:

views/photos/edit.html.erb

Bien, pero no lo suficiente: los usuarios aún no pueden remover una imagen subida. Para permitir esto, necesitaremos--adivina qué--otro complemento:

models/image_uploader.rb

Usa un atributo virtual llamado :remove_image, así que permítelo dentro del controlador:

photos_controller.rb

Ahora solo muestra una casilla para remover una imagen si un registro tiene un ajunto en lugar:

views/photos/_form.html.erb

Generando una Imagen de Viñeta

Actualmente mostramos imágenes originales, la cuál no es la mejor aproximación para vistas previas: las fotos podrían ser grandes y ocupar mucho espacio. Por supuesto, podrías simplemente emplear los atributos width y height de CSS, pero eso es una mala idea también. Ves, incluso si la imagen está establecida para ser pequeña usando estilos, el usuario aún necesitará descargar el archivo original, el cuál podría ser bastante grande.

Así pues, es mucho mejor generar una vista previa pequeña en el lado del servidor durante la carga inicial. Esto involucra dos complementos y dos gemas adicionales. Primero, coloca las gemas:

Image_processing es una gema especial creada por el autor de Shrine. Presenta algunos métodos helper de alto nivel para manipular imágenes. Esta gema, en turno, depende de mini_magick, un envoltorio Ruby para ImageMagick. Como habrás adivinado, necesitarás ImageMagick en tu sistema para poder ejecutar esta demostración.

Instala estas nuevas gemas:

Ahora incluye los complementos junto con sus dependencias:

models/image_uploader.rb

Processing es el complemento para manipular una imagen (por ejemplo, encogerlo, rotarlo, convertirlo a otro formato, etc.). Versions, sucesivamente, nos permite tener una imagen en diferentes variantes. Para esta demostración, dos versiones serán almacenadas, "original" y "thumb" (redimensionadas a 300x300).

Aquí está el código para procesar una image y almacenar sus dos versiones:

models/image_uploader.rb

resize_to_limit! es un método provisto por la gema image_processing. Simplemente encoge una imagen a 300x300 si es más grande y no hace nada si es más pequeña. Además, mantiene la relación de aspecto adicional.

Ahora cuando mostremos la imagen, necesitas proporcionar ya sea el argumento :original o :thumb al método image_url:

views/photos/_photo.html.erb

Lo mismo puede ser hecho dentro del formulario:

views/photos/_form.html.erb

Para borrar automáticamente los archivos procesados después de que la carga está completa, podrías añadir un complemento llamado delete_raw:

models/image_uploader.rb

Metadatos de la Imagen

Además de generar una imagen, también puedes recoger sus metadatos. Vamos a, por ejemplo, mostrar el tamaño original de la foto y tipo MIME:

views/photos/_photo.html.erb

¿Qué sobre estas dimensiones? Desafortunadamente, no están acomodados por defecto, pero esto es posible con un complemento llamado store_dimensions.

Dimensiones de Imagen

El complemento store_dimensions depende de la gema fastimage, así que engánchalo ahora:

No olvides ejecutar:

Ahora solo incluye el complemento:

models/image_uploader.rb

Y muestra las dimensiones usando los métodos width y height:

views/photos/_photo.html.erb

También, hay un método dimensions disponible que regresa un arreglo conteniendo ancho y alto (por ejemplo, [500,750]).

Moviendo a la Nube

Los desarrolladores regularmente eligen servicios en la nube para hospedar archivos cargados, y Shrine presenta tal posibilidad. En esta sección, te mostraré cómo subir archivos a Amazon S3.

Como el primer paso, incluye dos gemas más en el Gemfile:

aws-sdk es requerido para trabajar con el SDK de S3, mientras que dotenv-rails será usado para administrar variables de entorno en desarrollo.

Antes de proceder, deberías obtener un par de llaves para acceder a S3 vía API. Para ingresar, inicia sesión (o registrare) en la Amazon Web Services Console y navega a Credenciales de Seguridad > Usuarios. Crea un usuario con permisos para manipular archivos en S3. Aquí está la política simple presentando acceso completo a S3:

Descarga el par de llaves de usuario creadas. De manera alternativa, podrías usar llaves de acceso raíz, pero me te desaliento fuertemente a hacerlo ya que es muy inseguro.

Después, crea una cubeta S3 para hospedar tus archivos y agregar un archivo a la raíz del proyecto para hospedar tu configuración:

.env

Nunca expongas este archivo al público, y asegúrate de que lo excluyas de Git:

.gitignore

Ahora modifica la configuración global de Shrine e introduce un nuevo almacenamiento:

config/initializers/shrine.rb

¡Eso es! No se han hecho cambios a otras partes de la aplicación y puedes probar este nuevo almacenamiento de inmediato. Si estás recibiendo errores desde S3 relacionados con llaves incorrectas, asegúrate de que has copiado precisamente la llave y secreto, sin ningunos espacios ni símbolos especiales.

Conclusión

Hemos llegado al final de este artículo. Ojalá, ahora tengas más confianza usando Shrine y estés ansioso de emplearlo en uno de tus proyectos. Hemos discutido muchas de las características de esta gema, pero hay más, como la habilidad de almacenar contexto adicional junto con archivos y el mecanismo de carga directa.

Así pues, navega la documentación de Shrine y su sitio web oficial, el cuál describe a fondo todos los complementos disponibles. Si tienes otras preguntas sobre esta gema, no dudes en publicarlas. Te agradezco por quedarte conmigo, ¡y te veré 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.