Created
August 25, 2023 10:49
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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