Create placeholder images with sharp Node.js image processing library

I have been searching for a solution to pre-generate some placeholder images for image server I needed to develop that resizes images on S3. I though this would be a 15min job and quickly found out how very mistaken I was.

Even though Node.js is not really the best way to do this kind of things (surely something written in C or Rust or even Golang would be the correct way to do this but we didn't need the speed in our case) I found an excellent library sharp - High performance Node.js image processing.

Getting things running was a breeze.

Fetch image from S3 and save resized

const sharp = require('sharp');
const aws = require('aws-sdk');

const x,y = 100;
const s3 = new aws.S3({});

aws.config.update({
  secretAccessKey: 'secretAccessKey',
  accessKeyId: 'accessKeyId',
  region: 'region'
});

const originalImage = await s3.getObject({
  Bucket: 'some-bucket-name',
  Key: 'image.jpg',
}).promise();

const resizedImage = await sharp(originalImage.Body)
  .resize(x, y)
  .jpeg({ progressive: true })
  .toBuffer();

s3.putObject({
  Bucket: 'some-bucket-name',
  Key: `optimized/${x}x${y}/image.jpg`,
  Body: resizedImage,
  ContentType: 'image/jpeg',
  ACL: 'public-read'
}).promise();

All this code was wrapped inside a web service with some additional security checks and defensive coding to detect if key is missing on S3.

And at that point I needed to return placeholder images as a response in case key is missing or x,y are not allowed by the server etc. I could have created PNG in Gimp and just serve them but I wanted to respect aspect ratio and I didn't want to return some mangled images.

Main problem with finding a clean solution I could copy and paste and change a bit was a task. API is changing constantly and there weren't clear examples or I was unable to find them.

Generating placeholder images using SVG

What I ended up was using SVG to generate text and created image with sharp and used composition to combine both layers. Response returned by this function is a buffer you can use to either upload to S3 or save to local file.

const generatePlaceholderImageWithText = async (width, height, message) => {
  const overlay = `<svg width="${width - 20}" height="${height - 20}">
    <text x="50%" y="50%" font-family="sans-serif" font-size="16" text-anchor="middle">${message}</text>
  </svg>`;

  return await sharp({
    create: {
      width: width,
      height: height,
      channels: 4,
      background: { r: 230, g: 230, b: 230, alpha: 1 }
    }
  })
    .composite([{
      input: Buffer.from(overlay),
      gravity: 'center',
    }])
    .jpeg()
    .toBuffer();
}

That is about it. Nothing more to it. You can change the color of the image by changing background and if you want to change text styling you can adapt SVG to your needs.

Also be careful about the length of the text. This function positions text at the center and adds 20px padding on all sides. If text is longer than the image it will get cut.


Comment, contact: The easiest way to contact me is by writing me a message on Telegram (https://t.me/mitjafelicijan).

You can also just write me an email at m@mitjafelicijan.com.



Articles from blogs I follow around the net

Status update, July 2022

Hello there! It’s been a hot July week in Amsterdam, and I expect hotter days are still to come. I wish air conditioning was more popular in Europe, but alas. This mo…

via Drew DeVault's blog July 18, 2022
In defense of simple architectures

Wave is a $1.7B company with 70 engineers1 whose product is a CRUD app that adds and subtracts numbers. In keeping with this, our architecture is a standard CRUD ap…

via danluu.com April 6, 2022

Generated by openring