¿Cómo optimizar imágenes en Deno?

Guille Paz


Hace unos meses escribí sobre cómo mejoré la métrica LCP usando WebP en xstoregames.com creando una Serverless Function en Vercel.

Actualmente, estoy trabajando en la tienda de PlayStation usando Deno y 🍋 Fresh y quería mantener esta optimización para las imágenes.

El principal problema que encontré fue que sharp no tiene soporte completo para Deno. Por lo menos, leyendo y probando lo que mencionaban en los comentarios no lo pude hacer andar.

Luego de buscar un rato, encontré el módulo ImageMagick con soporte completo y fue super fácil.

ImageMagick al rescate

import {
  ImageMagick,
  MagickFormat,
  initializeImageMagick,
} from 'imagemagick';

await initializeImageMagick();

function optimizeImage(imageBuffer, format) {
  return new Promise(resolve => {
    const ib = new Uint8Array(imageBuffer);
    ImageMagick.read(ib, img => {
      img.quality = 70;
      img.write(
        d => resolve(d),
        format === 'webp' ? MagickFormat.Webp : MagickFormat.Jpg
     );
    });
  });
};

Antes: 1 MB

Después: 448 kB

En el servicio usando acorn

import {
  ImageMagick,
  MagickFormat,
  initializeImageMagick,
} from 'imagemagick';

await initializeImageMagick();

function optimizeImage(imageBuffer, format) {
  return new Promise(resolve => {
    const ib = new Uint8Array(imageBuffer);
    ImageMagick.read(ib, img => {
      img.quality = 70;
      img.write(
        d => resolve(d),
        format === 'webp' ? MagickFormat.Webp : MagickFormat.Jpg
     );
    });
  });
};

export default async (ctx) => {
  const path = ctx.params.path;
  const queryString = new URLSearchParams(ctx.searchParams).toString();
  const ps = `https://image.api.playstation.com/${path}?${queryString}`;

  const response = await fetch(ps).then(res => res.arrayBuffer());
  const format = ctx.request.headers.get('accept').includes('image/webp') ? 'webp' : 'jpeg';

  const data = await optimizeImage(response, format);

  return new Response(data, {
    status: 200,
    headers: {
      'Content-Type': `image/${format}`,
      'Content-Length': data.byteLength,
      'Cache-Control': 'public, max-age=31536000, immutable',
    },
  });
};

Chao. 🚀