WebP Cloud 提供一个接口,可以用于获取图片的元数据,比如长宽、大小、色彩空间以及blurhash。这部分计算,尤其是blurhash其实还是略有压力的,我们就想能不能把这部分功能放到回源请求上。
对于 Azure Function来说,这必然不是问题,因为他几乎就是一个完整的NodeJS环境,可以用sharp的;但是对于Workers,由于他是V8,只支持NodeJS最基本的一些API,最多带上Wasm,这样用sharp就变成了几乎不可能的事情,因为要调用libvips。
最终经过了我的艰苦探索,发现一个名为 @cf-wasm/photon
的库,可以用来获取图片基础信息。首先需要 import
import { PhotonImage, SamplingFilter, resize } from '@cf-wasm/photon';
用起来也还行,首先我们需要通过 fetch
获取图片,得到一个response
,可以从这里拿到 ArrayBuffer
const response = await fetch(url) const buffer = await response.arrayBuffer();
然后PhotonImage
需要Uint8Array
,那先转换一下
const inputBytes = new Uint8Array(buffer);
然后加载图片
const inputImage = PhotonImage.new_from_byteslice(inputBytes);
宽高可以用 inputImage.get_width()
和inputImage.get_height()
色彩空间可以用 inputImage.get_image_data().colorSpace
文件大小直接 buffer.length
就行
计算 blurhash建议先调整图片大小,毕竟 Worker有执行时间限制
const resized = resize(inputImage, 32, 32, SamplingFilter.Nearest);
然后计算
const blur = encode(resized.get_raw_pixels(), resized.get_width(), resized.get_height(), 4, 4);
至于图片格式,那只能靠magic header了,比如使用如下ChatGPT给我的神奇代码
function getImageFormatFromArrayBuffer(arrayBuffer) { const uint8Array = new Uint8Array(arrayBuffer); // Check for PNG (first 8 bytes: 89 50 4E 47 0D 0A 1A 0A) if ( uint8Array[0] === 0x89 && uint8Array[1] === 0x50 && uint8Array[2] === 0x4e && uint8Array[3] === 0x47 && uint8Array[4] === 0x0d && uint8Array[5] === 0x0a && uint8Array[6] === 0x1a && uint8Array[7] === 0x0a ) { return 'png'; } // Check for JPEG (first 3 bytes: FF D8 FF) if (uint8Array[0] === 0xff && uint8Array[1] === 0xd8 && uint8Array[2] === 0xff) { return 'jpeg'; } // Check for GIF (first 6 bytes: GIF87a or GIF89a) if ( uint8Array[0] === 0x47 && uint8Array[1] === 0x49 && uint8Array[2] === 0x46 && uint8Array[3] === 0x38 && (uint8Array[4] === 0x37 || uint8Array[4] === 0x39) && uint8Array[5] === 0x61 ) { return 'gif'; } // Check for BMP (first 2 bytes: 42 4D) if (uint8Array[0] === 0x42 && uint8Array[1] === 0x4d) { return 'bmp'; } // Check for WebP (first 4 bytes: 52 49 46 46 and "WEBP" in bytes 8-11) if ( uint8Array[0] === 0x52 && uint8Array[1] === 0x49 && uint8Array[2] === 0x46 && uint8Array[3] === 0x46 && uint8Array[8] === 0x57 && uint8Array[9] === 0x45 && uint8Array[10] === 0x42 && uint8Array[11] === 0x50 ) { return 'webp'; } return 'Unknown format'; }
部署
npm install @cf-wasm/photon npm install blurhash wrangler deploy
就可以了。wrangler会自动打包,把依赖和wasm也一起上传上去
Azure Function
如果你使用的是Azure Function,那么事情就简单多了,直接安装并使用 sharp 就行。需要注意的一点是,Azure Function可以选择运行的环境是Linux还是Windows。所以本地也要安装好正确的sharp然后才可以部署。
npm install --cpu=x64 --os=linux sharp
详情可以参考Cross-platform
最终结果
原来是打算把 Worker和Function 用同一套代码库的,但是由于 Worker的限制,即使不同情况下使用不同的import,Worker还是没法兼容 Function🫠
所以只能分开两个分支了。
Cloudflare Workers 太弱智了,害我失去了人生中宝贵的三个小时