Skip to content

Instantly share code, notes, and snippets.

@foru17
Created August 25, 2023 10:49
Show Gist options
  • Save foru17/9e847e9395b2577e3e5eb8965a36c3db to your computer and use it in GitHub Desktop.
Save foru17/9e847e9395b2577e3e5eb8965a36c3db to your computer and use it in GitHub Desktop.
A Cloudflare Workers script automatically uploads existing images to the Cloudflare Images service.
// Cloudflare Images 鉴权API文档
// https://developers.cloudflare.com/images/cloudflare-images/api-request/
const CF_IMAGES_ACCOUNT_ID: string = "<账号ID>";
const CF_IMAGES_API_KEY: string = "<CLOUDFLARE IMAGES API KEY>";
const CF_IMAGES_BUCKET_NAME: string = "<账号HASH>";
// 白名单域名
const ALLOWED_HOSTS: string[] = ["c2.llyz.xyz", "luolei.org"];
addEventListener("fetch", (event: FetchEvent) => {
event.respondWith(handleRequest(event.request));
});
// 处理 Workers 请求
async function handleRequest(request: Request): Promise<Response> {
const url = new URL(request.url);
// 获得传入的图片路径
const imageOriginURLPath = url.pathname.slice(1);
let imagePath = imageOriginURLPath;
const hostName: string = imagePath.split("/")[2];
const imageOriginURL: string = 'https://' + hostName + '/';
// 只允许白名单域名, 其他域名直接返回原始图片,也可以返回 403
if (!ALLOWED_HOSTS.includes(hostName)) {
return fetch(imageOriginURLPath, { headers: { "Accept": request.headers.get("accept")! } });
}
// 这里还可以优化参数处理逻辑
let params: string = imagePath.split("/").pop()!;
let splitPath = imagePath.split("/");
splitPath.pop();
imagePath = splitPath.join('/');
const customID: string = generateCustomID(imagePath);
try {
// 先从 Cloudflare Images 获取图片唯一地址
let cfImageUrl: string | null = await getCloudflareImageUrl(customID);
if (!cfImageUrl) {
// 如果不存在,上传图片到 Cloudflare Images
await upload(imagePath, imageOriginURL, customID);
cfImageUrl = await getCloudflareImageUrl(customID);
}
cfImageUrl = `https://imagedelivery.net/${CF_IMAGES_BUCKET_NAME}/${customID}`;
// 拼接参数
cfImageUrl += `/${params}`;
return fetch(cfImageUrl, { headers: { "Accept": request.headers.get("accept")! } });
} catch (error) {
console.error("Error fetching from Cloudflare:", error);
// 如果 Cloudflare Images 请求失败,返回原始图片
return fetch(imageOriginURLPath, { headers: { "Accept": request.headers.get("accept")! } });
}
}
// 生成 Cloudflare Images 唯一ID,简化处理,可以根据自己的需求修改
function generateCustomID(path: string): string {
return path.replace(/\//g, '_').replace(/\./g, '-');
}
async function getCloudflareImageUrl(customID: string): Promise<string | null> {
const res: Response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${CF_IMAGES_ACCOUNT_ID}/images/v1/${customID}`, {
method: "GET",
headers: { "Authorization": `Bearer ${CF_IMAGES_API_KEY}` }
});
if (res.ok) {
const data = await res.json();
return data.result.url;
}
return null;
}
async function upload(imagePath: string, imageOriginURL: string, customID: string): Promise<void> {
console.log(`Uploading to Cloudflare Images: ${imagePath}`);
const body = new FormData();
body.append("url", imageOriginURL + imagePath.split("/").slice(3).join("/"));
body.append("id", customID);
try {
const res: Response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${CF_IMAGES_ACCOUNT_ID}/images/v1`, {
method: "POST",
headers: { "Authorization": `Bearer ${CF_IMAGES_API_KEY}` },
body
});
if (res.status !== 200 && res.status !== 409) {
throw new Error("HTTP " + res.status + " : " + await res.text());
}
if (res.status === 409) {
console.log("Already exists: " + imagePath);
}
} catch (e) {
throw Error(e);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment