Accede a todo EDteam con un único pago¡Sube a premium!

Optimizando imágenes en minutos con Bash y Tinypng

Hace tiempo estaba trabajando sobre un proyecto donde se buscaba optimizar un una plataforma, realmente para hacerlo se requiere hacer muchas cosas pero una de las más importantes es optimizar el tamaño de imágenes...

Diseño web
10 minutos
Hace 6 años
Optimizando imágenes en minutos con Bash y Tinypng

Hola chicos de la comunidad EDTeam!

Hace tiempo estaba trabajando sobre un proyecto donde se buscaba optimizar un una plataforma, realmente para hacerlo se requiere hacer muchas cosas pero una de las más importantes es optimizar el tamaño de imágenes, por buenas prácticas y sobre todo para los que viven en LATAM donde las conexiones 3g y 4g rara vez andan bien y el costo de datos es algo caro, se vuelve impredecible tener que hacer algo con ellas.

Páginas lentas = menos visitantes, a menos que se trate de páginas del gobierno una buena excepción (sarcasmo)

Actualmente me encuentro trabajando en linux hay varias opciones para realizar este cometido desde utilizar algún programa de diseño como gimp, usar gulp o webpack junto con algún plugin o inclusive en google developers recomiendan utilizar jpegtran, OptiPNG o PNGOUT pero son compresores independientes para cada tipo de extensión

El verdadero problema es que tenía alrededor de 1500 imágenes, un trabajo bastante laborioso para comprimirlas una por una en gimp o photoshop. siempre he utilizado tinypng para esta tarea pero en esta ocasión eran bastantes imágenes para ir comprimiendo de 20 en 20 en la plataforma, así que tocaría usar su API para evitar esa restricción y la tediosa tarea de estarlas subiendo a su web.

1-. ¿Qué Necesitaremos?

1. Unos cuantos comando de unix shell

Usaremos algunos comandos básicos que se usan dentro de nuestra terminal.

2. Curl

También podríamos usar wget pero como se trata de consumir recursos de una api, curl va bastante bien además de que es una librería que se encuentra integrada en casi cualquier lenguaje y soporta la transferencia de data por un montón de protocolos (para consumir los datos de tinypng será por HTTP).

puedes verificar que lo tengas instalado con:

curl -v

3. Bash

Bash es un poderosa aplicación que nos provee de un lenguaje de scripting que se integra perfectamente con los comandos de unix así que si la consola es poderosa aumentamos sus posibilidades con Bash.

¿Hola mundo en bash?, dirígete a tu consola y escribe

echo "hola mundo desde bash :)"

4. JSON Processor

Es una librería que utilizaremos para interpretar el cuerpo de las respuestas de nuestra api por consola, ya que llegan en formato JSON.

jq no viene instalada por defecto en sistemas linux por lo tanto hay que instalarla con el clásico

sudo apt-get install jq

Les dejo el link de la libreria se encuentra disponible para múltiples plataformas y distribuciones

Para testear que funcione podemos rápidamente recoger por curl un fake JSON y usar un pipe para imprimir el resultado.

curl https://jsonplaceholder.typicode.com/posts/1 | jq .

testjq

5. API Tinypng

Debemos ir a la página oficial de tinypng developers y pedir una API key para conectarnos a la api, es muy sencillo solo nos pide una cuenta de correo y un nombre de usuario, te envía un correo y te dirige al dashboard con tu llave.

tinyapikey

2. Manos a la obra!

Lo primero que vamos a hacer es construir una carpeta en cualquier lugar de tu pc y crear 2 carpetas en donde pondremos las imágenes originales y otra donde se pondran las imagenes comprimidas, no olvidemos crear nuestro archivo bash para escribir nuestro script y agregar un par de imágenes en la carpeta originales para realizar pruebas.

La estructura queda así:

├── optimizadas
├── compressor.sh
└── originales
   ├── 1.jpg
   ├── 2.jpg
   ├── 3.jpeg
   ├── 4.png
   └── 5.png

¿Cúal es el plan?

Lo que vamos hacer es escribir un script en Bash en donde tomemos las imágenes de nuestro folder originales y con la ayuda de la API de tinypng comprimirlas y descargar los archivos optimizados en la carpeta optimizadas, Sí, así de sencillo como parece!

¿Cómo funciona el API de tinypng?

Básicamente nos pide enviar una petición POST con el archivo de imágen y nos regresa una respuesta con información de el tamaño original de la imagen, tamaño de la imagen después de la compresión y una url para descargar el archivo.

Por obvias razones debemos hacer una petición GET a esa "url" para descargar la imagen comprimida.

3. Escribiendo el script

Siguiendo un poco la ideología de EDteam comenzaremos "desde 0".

Primero comenzamos declarando variables que vamos a utilizar en el script

# ESTABLECER LOS PATHS DE NUESTRAS CARPETAS
FILES_PATH=$PWD'/originales'
FILES_OP_PATH=$PWD'/optimizadas'

# AQUI PEGAMOS NUESTRA APIKEY DE TINYPNG
KEY_API='BkffSQyiWN6dAJQpeg8xQtk1mjiNaRQk'

Escribiré nuestras variables en mayúsculas para no confundirnos, en el caso de las rutas para nuestras carpetas utilizaremos paths absolutos, hacemos uso de nuestra variable reservada $PWN qué es lo mismo que escribir pwd en consola.

Después de esto vamos a obtener todas nuestras imágenes de nuestra carpeta originales haciendo uso de un ciclo for, en Bash los ciclos son similares a python en donde es muy fácil iterar colecciones.

for i in $(ls $FILES_PATH); do
   # código ...
done

Como se nota en el código todas las instrucciones encerradas en $( ... ) serán ejecutadas en un entorno subshell y darán como salida estándar el resultado del comando, es por eso que usamos $(ls $FILES\_PATH) para que nos regrese el listado de imágenes que hay en la carpeta.

Ahora que tenemos todas las imágenes de la carpeta procedemos a subirlas.

Hacemos uso de CURL pasando el APIKEY y la ruta de nuestra imágen como data binaria, nota que $i contiene el nombre de la imágen. Una vez se obtenga el response de la api lo escribimos en un archivo up.json que no existe pero contendrá la información correspondiente a la imágen.

$(curl https://api.tinify.com/shrink --user api:$KEY_API --data-binary @$FILES_PATH/$i > up.json)

Si todo ha salido bien en este punto ya tendremos la imagen lista para descargar, entonces procedemos a extraer la información del archivo up.json que guardamos con JSON processor.

up.json se ve la siguiente manera:

{
 "input": {
   "size": 108363,
   "type": "image/png"
 },
 "output": {
   "size": 35056,
   "type": "image/png",
   "width": 1920,
   "height": 1200,
   "ratio": 0.3235,
   "url": "https://api.tinify.com/output/rtgr7y81v7j051bk0ag540w5d17bj26a"
 }
}

Input es la información de la imagen sin comprimir y output contiene la información de la imágen después de comprimir.

Ahora extraemos la información que necesitemos, para descargar la imágen y para generar un reporte

# TAMAÑO CON EL QUE SE SUBIO
UPLOAD_SIZE=$(cat up.json | jq -r '.input.size')
# TAMAÑO DESPUES DE OPTIMIZAR
DOWNLOAD_SIZE=$(cat up.json | jq -r '.output.size')
# URL PARA DESCARGAR LA IMAGEN
URL_DOWNLOAD=$(cat up.json | jq -r '.output.url')

Para obtener los datos del json es muy sencillo solamente utilizamos la instrucción cat sobre up.json para recibir como salida la información del archivo y enlazamos una tubería "|" para leer los atributos del json, según nos marca la estructura debemos navegar sobre input y output para recoger los valores correspondientes tal y como lo hacemos con un archivo JSON común y corriente.

Finalmente procedemos a descargar nuestra imagen optimizada, haciendo una petición GET a la url que sacamos del json y pasando como parámetro de salida el path de nuestra carpeta optimizadas junto con el nombre original de la imágen que viene con $i

$(curl $URL_DOWNLOAD --user api:$KEY_API --output $FILES_OP_PATH/$i)

Adicionalmente podemos generar un reporte en un archivo de texto para saber cuánto porcentaje ahorramos al comprimir los archivos.

Para esto simplemente volcamos la salida de forma que no se rescriba el archivo con ">>"

# ESCRIBIMOS EN UNA VARIABLE EL MENSAJE DE REPORTE
INFO_BASH="FILE: $i | ORIGINAL SIZE: $UPLOAD_SIZE => OPTIMIZED SIZE: $DOWNLOAD_SIZE"

# VOLCAMOS LA SALIDA EN info.txt que no existe
echo $INFO_FILE >> "info.txt"

4. Resultados

Para concluir nuestro script podemos agregar una variable acumuladora como contador para saber qué número de imágen está comprimiendo en tiempo de ejecución y algunos mensajes informativos extra.

Nuestro archivo finalmente quedaría de la siguiente manera:

#!/bin/bash
FILES_PATH=$PWD'/originales'
FILES_OP_PATH=$PWD'/optimizadas'
LOG_FILE='info.txt'
KEY_API='BkffSQyiWN6dAJQpeg8xQtk1mjiNaRQk'

COUNTER=0
for i in $(ls $FILES_PATH); do
   COUNTER=$[$COUNTER +1]
   echo "==============================="
   echo "============== $COUNTER ============="
   echo "==============================="
   $(curl https://api.tinify.com/shrink --user api:$KEY_API --data-binary @$FILES_PATH/$i > up.json)
   UPLOAD_SIZE=$(cat up.json | jq -r '.input.size')
   DOWNLOAD_SIZE=$(cat up.json | jq -r '.output.size')
   URL_DOWNLOAD=$(cat up.json | jq -r '.output.url')

   $(curl $URL_DOWNLOAD --user api:$KEY_API --output $FILES_OP_PATH/$i)
   INFO_FILE="FILE: $i | ORIGINAL SIZE: $UPLOAD_SIZE => OPTIMIZED SIZE: $DOWNLOAD_SIZE";
   
   echo $INFO_FILE
   echo $INFO_FILE >> "info.txt"
done
echo "==============================="
echo "============== END ============"
echo "==============================="

Listo ya tenemos nuestro script listo para comprimir!

Solo queda ejecutarlo en nuestra terminal para eso solo escribimos:

source compressor.sh

Si son muchas imágenes prepárate un café y espera a que termine el proceso :D.

Si todo ha salido bien el script comenzará a enviar nuestras imágenes a tinypng y las guardará en nuestra carpeta optimizadas

comprimiendo

Una vez terminado el proceso podemos revisar los resultados del proceso de compresión, teniendo todas nuestras imágenes originales comprimidas en la carpeta optimizadas con el mismo nombre y nuestro archivo info.txt con el log que muestra la información de cada imágen.

logtxt

5. Conclusiones

Como pudieron darse cuenta al ocupar un sencillo script podemos automatizar la compresión de imágenes cuando tenemos muchas, como les comentaba al principio del artículo comprimi cerca de 1500 imágenes, si lo hiciéramos manualmente nos tardaríamos días en terminarlo.

En la API de tinypng solo puedes optimizar 500 imágenes para lo cual tuve que generar correos desde mi hosting personal solo para solicitar nuevas KEYAPIs y seguir con el proceso de compresión. Si vas a implementar la api en algún proyecto real deberías de pagar algunos dólares mensuales para aumentar el límite de cargas.

Al igual cabe mencionar lo potente que puede ser un lenguaje de scripting como bash, y sobre todo como lo mencionaba al principio las posibilidades se multiplican al usar junto a instrucciones de unix shell.

Saludos comunidad EDteam! y si hay oportunidad nos vemos en un siguiente artículo :).

Comentarios de los usuarios

Pregunta a ChatEDT