以前在自己的服务器上用PHP代码做了一个接口,服务器过期后就失效了。最近接触到 Cloudflare Workers,因为不用部署在服务器上,而且支持 HTTPS,每天有免费的10万次请求数,于是又在 Cloudflare Workers 上做了一个。

以下步骤及代码均来源于阿里云·通义大模型:


要使用 Cloudflare Workers 搭建一个获取必应每日美图的 API,你可以按照以下步骤进行:

  1. 注册并登录 Cloudflare 账户

    • 如果还没有 Cloudflare 账户,首先需要注册一个。
  2. 创建一个新的 Worker

    • 登录 Cloudflare 控制台后,进入 Workers 部分,点击“Create a service”按钮。
    • 输入你的 Worker 名称,然后点击“Create service”。
  3. 编写 Worker 代码

    • 在 Worker 编辑器中,你可以编写 JavaScript 代码来实现获取必应每日美图的功能。
    • 必应每日美图的 URL 是 https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=en-US,你可以通过这个 URL 获取图片信息。
  4. 示例代码

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
// 假设的必应每日美图页面URL,实际上你需要用真实的URL替换它。
const bingDailyImageURL = 'https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN';

// 我们首先请求必应的API以获得今日美图的具体URL
const response = await fetch(bingDailyImageURL);
const data = await response.json();

// 构建完整的图片URL
const baseUrl = 'https://s.cn.bing.net';
const imageUrl = `${baseUrl}${data.images[0].url}`;

// 返回302重定向响应
return Response.redirect(imageUrl, 302);
}
  1. 部署 Worker

    • 编写完代码后,点击“Save and Deploy”按钮将你的 Worker 部署到 Cloudflare 的边缘网络。
  2. 测试 API

    • 部署完成后,你可以通过浏览器或者工具(如 Postman)访问你的 Worker URL 来测试 API 是否正常工作。
  3. 设置自定义域名(可选)

    • 如果你有自己的域名,可以在 Cloudflare 中添加域名,并将 Worker 关联到这个域名上,以便通过自定义域名访问你的 API。

通过以上步骤,你就可以成功搭建一个使用 Cloudflare Workers 获取必应每日美图的 API。


上面的代码我替换了国内访问速度较快的必应地址。

然后又想增加通过参数来获取不同分辨率的图片,于是修改了一下代码:

初步优化的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
// 定义支持的分辨率映射 (height -> width)
const supportedResolutions = {
'1080': '1920',
'720': '1280',
'768': '1024',
'600': '800',
'480': '640'
};

// 解析请求中的查询参数
const url = new URL(request.url);
const h = url.searchParams.get('h'); // 获取'h'参数

// 如果没有提供'h'参数或提供的值不在支持的列表中,则使用默认高度
const defaultResolution = '1080';
const height = h && supportedResolutions[h] ? h : defaultResolution;
const width = supportedResolutions[height];

// 获取必应每日美图信息
const bingDailyImageDataURL = 'https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN';
const response = await fetch(bingDailyImageDataURL);
const data = await response.json();

// 构建完整的图片URL
const baseUrl = 'https://s.cn.bing.net/';
let imageUrl = `${baseUrl}${data.images[0].url}`;

// 必应提供的原始链接可能是基于1920x1080分辨率的
// 我们需要根据请求的高度调整URL
const defaultResolutionStr = '1920x1080';
const requestedResolutionStr = `${width}x${height}`;

// 替换默认分辨率标识符为请求的高度对应的分辨率
if (imageUrl.includes(defaultResolutionStr)) {
imageUrl = imageUrl.replace(defaultResolutionStr, requestedResolutionStr);
} else {
// 如果URL不包含默认分辨率标识符,尝试通过其他方式获取所需分辨率图片
// 注意:这一步取决于必应的图片链接格式,可能需要根据实际情况调整
const imageBase = imageUrl.split('_').slice(0, -1).join('_'); // 假设图片链接是按某种模式命名的
imageUrl = `${imageBase}_${requestedResolutionStr}.jpg`;
}

// 返回302重定向响应到指定高度的图片
return Response.redirect(imageUrl, 302);
}

这个代码首先定义了一个对象,用于存储支持的分辨率映射。然后,它从请求的查询参数中获取’h’参数,并检查它是否存在于支持的分辨率映射中。如果不存在,则使用默认的分辨率。

接着优化:每天第一次访问时,会请求必应的API,获取今日美图的信息,然后根据请求的分辨率参数,构建完整的图片URL,然后把构建好的URL作为重定向目标返回给客户端,同时缓存到KV中,下次再访问时,直接从KV中读取图片URL,减少对必应的请求次数。但是这样一来,跟必应的API更新时间有一点差距,但无伤大雅。

优化完成的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});

// 直接指定KV命名空间
const kvNamespace = globalThis['bing.worker'];

// 定义支持的分辨率映射 (height -> width),作为全局变量
const supportedResolutions = {
'1080': '1920',
'720': '1280',
'768': '1024',
'600': '800',
'480': '640',
'2160': '3840', // UHD: 3840x2160
'1920': '1080', // FHD: 1080x1920
'1280': '720' // HD: 720x1280
};

// 特殊分辨率标识符映射
const specialResolutionIdentifiers = {
'3840x2160': 'UHD'
};

async function handleRequest(request) {
try {
const url = new URL(request.url);
const h = url.searchParams.get('h') || '1080'; // 默认高度
const requestedResolutionStr = `${supportedResolutions[h] || '1920'}x${h}`;

// 首先尝试从KV中获取指定分辨率的图片链接
const cachedImageUrl = await kvNamespace.get(requestedResolutionStr, 'text');

if (cachedImageUrl) {
// 如果缓存中存在,则直接返回
return Response.redirect(cachedImageUrl, 302);
}

// 如果缓存中不存在,则从必应API获取图片链接并构建所有支持分辨率的图片链接
const imageLinks = await fetchAndCacheBingDailyImage();

// 构建并返回请求的图片链接
const imageUrl = imageLinks[requestedResolutionStr] || imageLinks['1920x1080']; // 默认回退到1920x1080

// 返回新的图片链接给客户端
return Response.redirect(imageUrl, 302);
} catch (error) {
console.error('Error handling request:', error);
return new Response('Internal Server Error', { status: 500 });
}
}

async function fetchAndCacheBingDailyImage() {
try {
// 使用原始必应API获取每日美图数据
const bingDailyImageDataURL = 'https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN';
const response = await fetch(bingDailyImageDataURL);

if (!response.ok) {
throw new Error(`Failed to fetch Bing daily image: ${response.statusText}`);
}

const data = await response.json();
const baseUrl = 'https://cn.bing.com';
let originalImageUrl = `${baseUrl}${data.images[0].url}`;

// 清理URL,移除不必要的参数
const cleanedImageUrl = cleanImageUrl(originalImageUrl);

// 构建所有支持分辨率的图片链接
const imageLinks = Object.entries(supportedResolutions).reduce((acc, [height, width]) => {
const resolutionStr = `${width}x${height}`;
// 检查是否是特殊分辨率,并使用对应的标识符替换默认分辨率字符串
const replacement = specialResolutionIdentifiers[resolutionStr] || resolutionStr;
acc[resolutionStr] = cleanedImageUrl.replace('1920x1080', replacement);
return acc;
}, {});

// 将所有支持分辨率的图片链接存储到KV中,设置8小时的过期时间
await Promise.all(
Object.keys(imageLinks).map(resolution =>
kvNamespace.put(resolution, imageLinks[resolution], { expirationTtl: 8 * 60 * 60 })
)
);

return imageLinks;
} catch (error) {
console.error('Error fetching and caching Bing daily image:', error);
throw error;
}
}

function cleanImageUrl(url) {
try {
// 解析URL并移除特定的查询参数
const urlObj = new URL(url);
// 移除不需要的查询参数(如rf和pid)
urlObj.searchParams.delete('rf');
urlObj.searchParams.delete('pid');

// 返回清理后的URL字符串
return urlObj.toString();
} catch (error) {
console.error('Error cleaning URL:', error);
throw error;
}
}

使用/?h=720请求这个API,就可以得到一个302重定向到1280x720分辨率的图片。

效果如下:

必应每日美图
必应每日美图

API 已部署在 Cloudflare 上,可以通过访问这个链接来获取不同分辨率的图片。如果不附带参数或请求的参数不在支持的列表中,则返回默认的分辨率(1920x1080)。

参数对应分辨率
21603840x2160
19201080x1920
1280720x1280
10801920x1080
7201280x720
7681024x768
600800x600
480640x480

PS:workers的默认域名workers.dev在国内无法直接访问,需要设置自定义域名后才能访问。