快捷菜单
常用功能一站直达
更多功能请点顶栏「快捷菜单」
腾讯云 EdgeOne 边缘函数实现七牛云 & S3 存储随机图床 API

次文章来源@十三的《腾讯云 EdgeOne 边缘函数实现七牛云 & S3 存储随机图床 API》文章。感谢大佬的文章❤️❤️❤️。
记录于2026年7月14日。
个人静态博客存在壁纸分层展示需求,PC 端宽屏壁纸、移动端竖屏壁纸需要两套独立图库,前端直连对象存储存在多重业务痛点:
依托腾讯云 EdgeOne 边缘函数搭建代理中间层,边缘节点直出图片二进制流,天然实现 PC / 移动端 图库物理隔离、边缘缓存降费、统一跨域处理、全链路异常兜底,无需后端服务即可完成可落地随机图床 API。
| 对比维度 | 302 重定向方案 | EdgeOne 边缘节点代理直出图片流 |
|---|---|---|
| 流量链路 | 用户 → 边缘节点 → 302 跳转 → 对象存储源站 | 用户 → 就近边缘节点,边缘回源存储拉取图片,二进制流直接返回客户端 |
| 缓存能力 | 仅缓存 302 响应头,图片本体无法缓存 | 完整缓存图片二进制数据,同区域重复访问无源站流量 |
| 密钥安全 | 前端可捕获存储 CDN 域名,AK / SK 存在泄露盗刷风险 | 密钥存放边缘环境变量,前端无法感知存储地址与鉴权信息 |
| 跨域运维 | 每个存储桶单独配置 CORS,多存储场景运维繁琐 | 边缘统一输出跨域响应头,一套规则适配全部 S3 兼容存储 |
| 异常降级 | 资源失效直接返回 404,无兜底展示逻辑 | 边缘捕获全链路异常,自动切换预设兜底图片返回 |
| 长期成本 | 源站下行流量持续消耗,存储计费偏高 | 边缘缓存命中占比提升后,源站流量成本大幅下降 |
优先选用 EdgeOne 边缘节点代理直出图片流 架构。 该方案在存储安全、流量成本、运维复杂度、异常容错四大维度全面优于 302 重定向,适配静态博客、轻量化站点高频调用随机图床接口的长期运营场景,零后端架构大幅降低服务器运维成本。
+−↺⛶
/api 或 /m-api 接口请求,流量调度至就近 EdgeOne 边缘节点;blog/bg/,存放 16:9 / 21:9 宽屏高清壁纸,适配桌面端展示;blog/bg-m/,存放 9:16 竖屏壁纸,适配手机、平板竖屏场景;
⚠️ 关键避坑警告:生产环境严禁将 AK、SK 密钥硬编码写入边缘函数源码,必须通过 EdgeOne 环境变量配置密钥,防止源码泄露导致存储文件被盗刷、产生高额流量账单。
api.img.你的备案域名.comapi.img.你的备案域名.com


img.你的备案域名.com
1/** * 随机图 API - 腾讯云 EdgeOne 大满贯版 * 特性:直连七牛云目录、极速内存缓存、边缘代理(零跨域、非302) */
2// ==========================================// 1. 核心配置区// ==========================================const CONFIG = { AK: "AK密钥", SK: "SK密钥", BUCKET: "七牛云存储空间名称", DOMAIN: "最好填写套一层CDN的,EO的域名", // 你的图床 CDN 域名
3 PC_PREFIX: "blog/bg/", MOBILE_PREFIX: "blog/bg-m/"};
4// ==========================================// 2. 全局内存缓存// ==========================================let pcImages = [];let mobileImages = [];let lastCheckTime = 0;const CACHE_TTL = 3 * 60 * 1000; // 3 分钟缓存
5addEventListener('fetch', event => { event.respondWith(handleRequest(event.request))});
6// ==========================================// 3. 密码学:生成七牛云鉴权 Token// ==========================================async function generateQiniuToken(path) { const signingStr = `${path}\n`; const encoder = new TextEncoder(); const key = await crypto.subtle.importKey( 'raw', encoder.encode(CONFIG.SK), { name: 'HMAC', hash: 'SHA-1' }, false, ['sign'] ); const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(signingStr)); const base64 = btoa(String.fromCharCode(...new Uint8Array(signature))); const encodedSign = base64.replace(/\+/g, '-').replace(/\//g, '_'); return `QBox ${CONFIG.AK}:${encodedSign}`;}
7// ==========================================// 4. 抓取七牛云真实文件列表// ==========================================async function fetchImagesFromQiniu(prefix) { try { const apiPath = `/list?bucket=${CONFIG.BUCKET}&prefix=${prefix}&limit=1000`; const apiHost = 'rsf.qiniuapi.com'; const token = await generateQiniuToken(apiPath);
8 const response = await fetch(`https://${apiHost}${apiPath}`, { headers: { 'Authorization': token, 'Content-Type': 'application/x-www-form-urlencoded' } });
9 if (!response.ok) return [];
10 const data = await response.json(); if (!data.items) return [];
11 return data.items .filter(item => item.mimeType && item.mimeType.startsWith('image/')) .map(item => `${CONFIG.DOMAIN}/${item.key}`);
12 } catch (error) { console.error(`拉取目录 ${prefix} 失败:`, error); return []; }}
13// ==========================================// 5. 代理获取图片并注入跨域头 (核心新增逻辑)// ==========================================async function proxyImage(imageUrl) { try { // 边缘节点出面去你的 CDN 拿图 const response = await fetch(imageUrl, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) EdgeOne-Proxy" } });
14 if (response.ok) { // 拿到图片后,重写响应头,强行允许跨域 const newHeaders = new Headers(response.headers); newHeaders.set("Access-Control-Allow-Origin", "*"); newHeaders.set("Cache-Control", "no-cache"); // 告诉浏览器不要缓存当前API链接,以免下次拿不到新图
15 // 返回 200 状态码和真实图片数据流 return new Response(response.body, { status: 200, headers: newHeaders }); }
16 // 如果图片代理失败,降级回 302 重定向 return Response.redirect(imageUrl, 302); } catch (e) { // 网络异常,降级回 302 重定向 return Response.redirect(imageUrl, 302); }}
17// ==========================================// 6. 主路由逻辑// ==========================================async function handleRequest(request) { const url = new URL(request.url); const route = url.pathname;
18 // 初始化或更新缓存 if (Date.now() - lastCheckTime > CACHE_TTL || pcImages.length === 0 || mobileImages.length === 0) { try { const [newPc, newMobile] = await Promise.all([ fetchImagesFromQiniu(CONFIG.PC_PREFIX), fetchImagesFromQiniu(CONFIG.MOBILE_PREFIX) ]);
19 if (newPc.length > 0) pcImages = newPc; if (newMobile.length > 0) mobileImages = newMobile; lastCheckTime = Date.now(); } catch (e) { console.error("更新列表失败,暂时使用旧缓存"); } }
20 // --- 处理电脑壁纸 --- if (route === '/api') { if (pcImages.length === 0) return new Response("初始化中...", {status: 503}); const randomImg = pcImages[Math.floor(Math.random() * pcImages.length)]; // 调用代理函数,直接返回图片流 return await proxyImage(randomImg); }
21 // --- 处理手机壁纸 --- if (route === '/m-api') { if (mobileImages.length === 0) return new Response("初始化中...", {status: 503}); const randomImg = mobileImages[Math.floor(Math.random() * mobileImages.length)]; // 调用代理函数,直接返回图片流 return await proxyImage(randomImg); }
22 return new Response("API 正常运行。电脑壁纸请访问 /api,手机壁纸请访问 /m-api", { status: 200, headers: { "Content-Type": "text/html;charset=UTF-8" } });}展开
^/api.*、^/m-api.*,匹配域名选择已接入业务站点域名api.img.你的备案域名.com;

https://api.img.你的备案域名.com/api,多次刷新随机返回 PC 宽屏壁纸二进制图片;

https://api.img.你的备案域名.com/m-api,随机返回移动端竖屏适配图片;

/test 访问,接口返回 404 文本提示,路由匹配逻辑正常。Cache-Control: public,max-age=86400 缓存标识;Created with ❤️ by 十三
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
分享打赏
腾讯云 EdgeOne 边缘函数实现七牛云 & S3 存储随机图床 API
https://blog.nw177.cn/posts/40-博客搭建/07-腾讯云-edgeone-边缘函数实现七牛s3-随机图床-api/
作者
十三
发布于
2026-07-13
许可协议
日历 · 精选 · 友链 · 更多
如果内容对你有帮助,欢迎请我喝杯咖啡 ☕



One的公众号
爱折腾博客的小白