Processing Images with Cloudflare Worker

Background

Previously, I set up a 10GB storage, unlimited bandwidth cloud storage using Backblaze B2 and Cloudflare, which I use for daily file sharing and as an image hosting service for my blog. It works well with uPic. However, when using it as an image hosting service for my blog, I found that it doesn’t support image resizing/cropping. I often use Alibaba Cloud OSS for image processing at work, and I couldn’t stand the limitation, so I decided to create my own service.

The free version of Workers only has a CPU limit of 10ms, and it frequently exceeds the resource usage limit, resulting in a high rate of image cracking. Now it has been adapted to Vercel Edge, which can be used with a CDN. See https://chi.miantiao.me/post/cloudflare-worker-image/

Process

After some research, I considered two options:

  1. Use Cloudflare to proxy Vercel Image. With this option, the traffic goes through Cloudflare -> Vercel -> Cloudflare -> Backblaze, which is not ideal in terms of stability and speed. Additionally, it only allows 1000 image processing requests per month, which is quite limited.

  2. Use the public service wsrv.nl. With this option, the traffic goes through Cloudflare -> wsrv.nl -> Cloudflare -> Backblaze, and the domain is not under my control. If I want to control the domain, I would have to go through Cloudflare Worker again, which adds complexity.

Since neither option was ideal, I kept looking for alternatives. Last week, when I was working on an Email Worker, I discovered that Cloudflare Worker supports WebAssembly (Wasm), which sparked the idea of using Worker + WebAssembly to process images.

Initially, I wanted to use sharp, which I had used when working with Node.js. However, the author mentioned that Cloudflare Worker does not support multithreading, so sharp cannot run on Cloudflare Worker in the short term.

I searched online and found that a popular Rust library for image processing is Photon, and there is also a demo in the community. I tried it out and confirmed that it can run on Cloudflare Worker. However, the demo has two drawbacks:

  1. Photon needs to be manually updated and cannot keep up with the official updates as quickly.
  2. It can only output images in PNG format, and the file size of JPG images actually becomes larger after resizing.

Result

Based on the keywords “Photon + Worker”, I did further research and came up with a new solution inspired by DenoFlare and jSquash. In the end, I used the official Photon (with patch-package as a dependency), Squash WebAssembly, and Cloudflare Worker to create an image processing service for resizing images. I originally wanted to support output in AVIF and JPEG XL formats, but due to the 1MB size limit of the free version of Workers, I had to give up this feature.

Supported features:

  1. Supports processing of PNG, JPG, BMP, ICO, and TIFF format images.
  2. Can output images in JPG, PNG, and WEBP formats, with WEBP being the default.
  3. Supports pipelining, allowing multiple operations to be executed.
  4. Supports Cloudflare caching.
  5. Supports whitelisting of image URLs to prevent abuse.
  6. Degrades gracefully in case of exceptions, returning the original image (exceptions are not cached).

Demo

Format Conversion

webp

webp

jpg

jpg

png

png

Resizing

resize

Rotation

rotate

Cropping

rotate

Filters

filter

Image Watermark

watermark

Text Watermark

draw_text

Pipeline Operations

Resize + Rotate + Text Watermark

resize & rotate & draw_text

Resize + Image Watermark

resize & watermark

In theory, it supports all the operations of Photon. If you are interested, you can check the image URLs and modify the parameters according to the Photon documentation to try it out yourself. If you encounter any issues, feel free to leave a comment and provide feedback.

Sharing

I have open-sourced this solution on my GitHub. If you need it, you can follow the documentation to deploy it.

ccbikai/cloudflare-worker-image - GitHub


Buy Me A Coffee

Gerardo ICS

© 2024 Gerardo Rodriguez

Linkedin GitHub