背景
之前使用 Backblaze B2 和 Cloudflare 搞了一个 10G 存储不限流量的网盘,用作日常文件分享和博客图床,配合 uPic 用起来也很顺手。但是在做博客图床的时候,有个遗憾不支持缩放/剪裁图片,工作中使用阿里云 OSS 处理图片特别顺手,有些忍不了,就想着自己搞一个服务。
Worker 的免费版本只能占用 10ms CPU,经常资源超出占用,裂图频率很高。现在适配了 Vercel Edge,可以套一个 CDN 使用,见 https://chi.miantiao.me/posts/cloudflare-worker-image/
过程
调研了一下,备选了两个方案:
-
使用 Cloudflare 代理 Vercel Image。此方案流量需要经过 Cloudflare -> Vercel -> Cloudflare -> Backblaze 多个环节,稳定性和速度都不理想,而且每个月限额 1000 次处理有些寒酸。
-
使用 wsrv.nl 公共服务。流量需要经过 Cloudflare -> wsrv.nl -> Cloudflare -> Backblaze 环节依然较多,而且域名还不在自己控制下,如果要控制域名,就得再走一次 Cloudflare Worker,复杂度就更高了。
两个方案都不理想的情况下,就一直在观望。上周在写 Email Worker 的时候发现 Cloudflare Worker 支持 WebAssembly (Wasm),就萌生了使用 Worker + WebAssembly 处理图片的想法。
本想使用之前写 Node.js 时使用的 sharp 但是作者讲 Cloudflare Worker 不支持多线程,sharp 短期内无法运行在 Cloudflare Worker。
网上搜了一下 Rust 流行的类库有 Photon,而且社区也有 Demo。尝试了一下的确可以运行在 Cloudflare Worker。但是这个示例存在两个缺陷:
- Photon 需要手动更新,无法最快速度跟随官方更新,
- 只能输出 PNG 格式,JPG 图片缩小后体积反而更大了。
结果
根据 Photon + Worker 关键字又调研了以后,参考 DenoFlare 和 jSquash 做了一版新的方案,最终使用官方 Photon (依赖 patch-package)、Squash WebAssembly、Cloudflare Worker 做了一个图片缩放服务。本想支持输出 AVIF 和 JPEG XL 格式,但是由于免费版 Worker 有 1M 大小限制,只得作罢。
支持特性:
- 支持 PNG、JPG、BMP、ICO、TIFF 格式图片处理
- 可输出 JPG、PNG、WEBP 格式图片,默认输出 WEBP 格式图片
- 支持管道,可以执行多个操作
- 支持 Cloudflare 缓存
- 支持图片地址白名单,防滥用
- 异常降级,如果处理失败返回原图 (异常场景不缓存)
演示
格式转换
webp
jpg
png
缩放
旋转
剪裁
滤镜
图片水印
文字水印
管道操作
缩放+旋转+文字水印
缩放+图片水印
理论上支持 Photon 的各种操作,有兴趣的可以查看图片地址,按照 Photon 文档修改参数自己尝试。如果发现异常可以评论反馈给我。
分享
此方案已经开源在我的 GitHub,有需要的可以按照文档部署。