Introduction

In modern web development, we have a couple of approaches to serve our image files on public. The simplest way is to serve your images directly from your web server. If your website is popular and in high traffic, you can choose to serve your images behind CDN (like Cloudflare, Cloudfront, etc.) for saving the machine resource and network bandwidth on your server.

If you don't need resize for your images dynamically, things will become much easier. All you need to do is to resize your images in different sizes beforehand and save them in your storage.

The image quality, width, height and other effects need to be configured in advance.

What would that be if you need to handle dynamic resizing? You can resize your images every single time when there's a resizing request.

Image Filter Module with Nginx

However, the more effective solution is to cache our resized image variants so that subsequent requests for each variant are served from the cache, without resizing  these images again. We can also define another separate virtual server that performs image resizing, and proxy requests to it only if the requested image size is not already in the cache. We call this the responsive image server.

Read more at: https://www.nginx.com/blog/responsive-images-without-headaches-nginx-plus/

AWS with Lambda

If your images are stored in AWS S3, you can consider to use S3 event notifications and AWS Lambda for eager processing of images when a new object is created in a bucket.

Read more at: https://aws.amazon.com/tw/blogs/compute/resize-images-on-the-fly-with-amazon-s3-aws-lambda-and-amazon-api-gateway/

Image Resizing as a Service

Also, there are many mature cloud services providing images hosting, resizing and even cropping. Of course, most of them are paid solutions.

Resize and Crop Images on GCS

As what I've mentioned previously, you have a nice solution in AWS S3. But what if you store your images on Google Cloud Storage? Absolutely, you can find a similar solution like what we did in AWS. Google Cloud has a serverless service called Cloud Function (like Lambda in AWS).

But today, I'm going to introduce an alternative approach for resizing images on GCS to you.

App Engine provides the ability to manipulate image data using a dedicated Images service. The Images service can manipulate images, composite multiple images into a single image, convert image formats, provide image metadata such as format, width, height, and a histogram of color values.

The Images service on Google App Engine can accept image data directly from the app, or it can use a Google Cloud Storage value. Images stored in Cloud Storage and Cloud Blobstore can be up to the maximum allowed value for the respective service. The transformed image is returned directly to the app, and must be less than 32 megabytes.

It will return a magic URL that serves the image in a format that allows dynamic resizing and cropping, so you don’t need to store different image sizes on the server.

https://lh3.googleusercontent.com/93uhV8K2yHkRuD63KJxlTi7SxjHS8my2emuHmGLZxEmX99_XAjTN3c_2zmKVb3XQ5d8FEkwtgbGjyYpaDQg

With this magic image URL, you can add many parameters after this link for resizing, cropping, rotating, and so on. The best part of this solution is: This service is totally free! You only need to pay for the storage fee on GCS.

If you serve images from Google Cloud Storage, you cannot serve an image from two separate apps. Only the first app that calls get_serving_url on the image can get the URL to serve it because that app has obtained ownership of the image.
I haven't verified if the bandwidth is also excluded from the pricing. I can't find any price plan for this service though.  

For example, you can get a resized image with 256 pixels via:

https://lh3.googleusercontent.com/93uhV8K2yHkRuD63KJxlTi7SxjHS8my2emuHmGLZxEmX99_XAjTN3c_2zmKVb3XQ5d8FEkwtgbGjyYpaDQg=s256

Or a cropped image with 256 pixels via:

https://lh3.googleusercontent.com/93uhV8K2yHkRuD63KJxlTi7SxjHS8my2emuHmGLZxEmX99_XAjTN3c_2zmKVb3XQ5d8FEkwtgbGjyYpaDQg=s256-c

You can put many parameters together, starting with `=` and concat them with `-` if you have multiple parameters.

Unfortunately, the bad news is this API is only accessible on GAE environment. You can't call it from other servers even if you're using GCE or GKE.

So, to be able to get this magic URL, you need to deploy a proxy service on GAE. Here's a python version for demonstration, built with Flask. You can follow this repository and deploy it to your GAE environment directly.

https://github.com/albertcht/python-gcs-image

Here's the full parameters list for providing different functions with magic URL:

SIZE / CROP

  • s640 — generates image 640 pixels on largest dimension
  • s0 — original size image
  • w100 — generates image 100 pixels wide
  • h100 — generates image 100 pixels tall
  • s (without a value) — stretches image to fit dimensions
  • c — crops image to provided dimensions
  • n — same as c, but crops from the center
  • p — smart square crop, attempts cropping to faces
  • pp — alternate smart square crop, does not cut off faces (?)
  • cc — generates a circularly cropped image
  • ci — square crop to smallest of: width, height, or specified =s parameter
  • nu — no-upscaling. Disables resizing an image to larger than its original resolution.

ROTATION

  • fv — flip vertically
  • fh — flip horizontally
  • r{90, 180, 270} — rotates image 90, 180, or 270 degrees clockwise

IMAGE FORMAT

  • rj — forces the resulting image to be JPG
  • rp — forces the resulting image to be PNG
  • rw — forces the resulting image to be WebP
  • rg — forces the resulting image to be GIF
  • v{0,1,2,3} — sets image to a different format option (works with JPG and WebP)

Forcing PNG, WebP and GIF outputs can work in combination with circular crops for a transparent background. Forcing JPG can be combined with border color to fill in backgrounds in transparent images.

ANIMATED GIFs

  • rh — generates an MP4 from the input image
  • k — kill animation (generates static image)

Filters

  • fSoften=1,100,0: - where 100 can go from 0 to 100 to blur the image
  • fVignette=1,100,1.4,0,000000 where 100 controls the size of the gradient and 000000 is RRGGBB of the color of the border shadow
  • fInvert=0,1 inverts the image regardless of the value provided
  • fbw=0,1 makes the image black and white regardless of the value provided

MISC.

  • b10 — add a 10px border to image
  • c0xAARRGGBB — set border color, eg. =c0xffff0000 for red
  • d — adds header to cause browser download
  • e7 — set cache-control max-age header on response to 7 days
  • l100 — sets JPEG quality to 100% (1-100)
  • h — responds with an HTML page containing the image
  • g — responds with XML used by Google's pan/zoom