腾讯云 EdgeOne 边缘函数实现七牛云 & S3 存储随机图床 API
3605 字
18 分钟
腾讯云 EdgeOne 边缘函数实现七牛云 & S3 存储随机图床 API

腾讯云 EdgeOne 边缘函数实现七牛云 & S3 存储随机图床 API
1 序言 🖼️
个人静态博客存在壁纸分层展示需求,PC 端宽屏壁纸、移动端竖屏壁纸需要两套独立图库,前端直连对象存储存在多重业务痛点:
- 统一图库混存图片,随机接口返回图片比例与设备不匹配,页面展示效果差;
- 前端直接请求存储域名触发 CORS 跨域拦截,同时持续消耗源站下行流量,抬高存储计费;
- 对象存储真实域名、AK / SK 鉴权密钥暴露客户端,存在文件盗刷、资源泄露风险。
依托腾讯云 EdgeOne 边缘函数搭建代理中间层,边缘节点直出图片二进制流,天然实现 PC / 移动端 图库物理隔离、边缘缓存降费、统一跨域处理、全链路异常兜底,无需后端服务即可完成可落地随机图床 API。
2 业务需求与方案选型对比
2.1 项目约束
- 资源分离:PC、移动端图片池使用独立存储目录,通过请求路由自动绑定对应图库,前端无需额外传参区分设备;
- 跨域解决:前端页面直接调用接口,无需在存储侧配置 CORS,兼容 fetch、axios 等主流请求库;
- 缓存降费:边缘节点缓存图片二进制资源,减少七牛云 / S3 源站回源请求与下行流量;
- 异常容错:目录无图片、存储鉴权失败、网络超时、资源 404 场景自动返回兜底占位图,避免页面空白报错。
2.2 两种实现方案三线对比
| 对比维度 | 302 重定向方案 | EdgeOne 边缘节点代理直出图片流 |
|---|---|---|
| 流量链路 | 用户 → 边缘节点 → 302 跳转 → 对象存储源站 | 用户 → 就近边缘节点,边缘回源存储拉取图片,二进制流直接返回客户端 |
| 缓存能力 | 仅缓存 302 响应头,图片本体无法缓存 | 完整缓存图片二进制数据,同区域重复访问无源站流量 |
| 密钥安全 | 前端可捕获存储 CDN 域名,AK / SK 存在泄露盗刷风险 | 密钥存放边缘环境变量,前端无法感知存储地址与鉴权信息 |
| 跨域运维 | 每个存储桶单独配置 CORS,多存储场景运维繁琐 | 边缘统一输出跨域响应头,一套规则适配全部 S3 兼容存储 |
| 异常降级 | 资源失效直接返回 404,无兜底展示逻辑 | 边缘捕获全链路异常,自动切换预设兜底图片返回 |
| 长期成本 | 源站下行流量持续消耗,存储计费偏高 | 边缘缓存命中占比提升后,源站流量成本大幅下降 |
2.3 方案选型结论
优先选用 EdgeOne 边缘节点代理直出图片流 架构。 该方案在存储安全、流量成本、运维复杂度、异常容错四大维度全面优于 302 重定向,适配静态博客、轻量化站点高频调用随机图床接口的长期运营场景,零后端架构大幅降低服务器运维成本。
3 整体架构与运行链路
3.1 完整请求链路流程图文字说明
- 用户发起
/api或/m-api接口请求,流量调度至就近 EdgeOne 边缘节点; - 边缘函数匹配请求路由绑定对应资源目录,携带鉴权签名调用存储文件列表接口;
- 过滤图片格式后随机选取资源,边缘节点回源拉取图片二进制;
- 统一拼接跨域、长缓存响应头,图片流返回客户端并在边缘本地缓存;
- 链路任意环节触发异常时,自动加载兜底图片返回,保障页面正常渲染。
3.2 资源分层规则
- PC 端图库目录:
blog/bg/,存放 16<9>9> / 21<9>9> 宽屏高清壁纸,适配桌面端展示; - 移动端图库目录:
blog/bg-m/,存放 9<16>16> 竖屏壁纸,适配手机、平板竖屏场景; - 两套目录物理隔离,接口路由自动绑定对应目录,前端无需传递设备标识参数。
4 获取七牛云密钥(兼容标准 S3 存储)
- 七牛云密钥获取流程
- 登录七牛云控制台,进入个人中心 - 密钥管理页面;
- 复制 AccessKey(AK)、SecretKey(SK) 两组鉴权密钥;
- 记录存储空间名称、CDN 访问域名、线上兜底图片完整地址。
- S3 通用存储适配说明
阿里云 OSS、腾讯云 COS、MinIO 等标准 S3 对象存储仅需重写签名生成函数、替换存储列表请求域名,边缘代理整体业务逻辑无需改动。

⚠️ 关键避坑警告:生产环境严禁将 AK、SK 密钥硬编码写入边缘函数源码,必须通过 EdgeOne 环境变量配置密钥,防止源码泄露导致存储文件被盗刷、产生高额流量账单。
5 腾讯 EdgeOne 边缘函数
5.1 添加触发边缘函数的域名
- 进入 EdgeOne 控制台站点管理,接入业务域名并完成 DNS 解析生效;
- 域名配置,加速域名:
api.img.你的备案域名.com - 回源配置,IP 域名:119.29.29.29
- 回源配置,回源 HOST 头:
api.img.你的备案域名.com - 不使用模板,下一步解析 DNS 就可以
- 配置 SSL 证书,开启全站 HTTPS 强制跳转,关闭 HTTP 明文访问;
- 开启域名边缘函数执行权限,限制流量直接回源源站。

5.2 创建边缘函数
- 侧边栏选择边缘函数,新建函数,运行环境选择 创建 Hello World;

- 函数名称:api-img
- 描述:随机图床 API
- 函数代码:全部删除,填写函数代码,先空着创建部署也可以。

5.3 编辑函数代码(核心)
- 完整边缘函数源码
- AK 密钥、SK 密钥
- BUCKET 存储桶名称,例如 sinc-img
- DOMAIN,最好填写套一层 CDN 的,EO的域名
img.你的备案域名.com - PC_PREFIX 电脑壁纸存储路径,例如 “blog/bg/”
- MOBILE_PREFIX 手机端壁纸存储路径,例如 “blog/bg-m/”
- 所以,你上传电脑壁纸和手机壁纸,就分别传这俩路径就可以

/** * 随机图 API - 腾讯云 EdgeOne 大满贯版 * 特性:直连七牛云目录、极速内存缓存、边缘代理(零跨域、非302) */
// ==========================================// 1. 核心配置区// ==========================================const CONFIG = { AK: "AK密钥", SK: "SK密钥", BUCKET: "七牛云存储空间名称", DOMAIN: "最好填写套一层CDN的,EO的域名", // 你的图床 CDN 域名
PC_PREFIX: "blog/bg/", MOBILE_PREFIX: "blog/bg-m/"};
// ==========================================// 2. 全局内存缓存// ==========================================let pcImages = [];let mobileImages = [];let lastCheckTime = 0;const CACHE_TTL = 3 * 60 * 1000; // 3 分钟缓存
addEventListener('fetch', event => { event.respondWith(handleRequest(event.request))});
// ==========================================// 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}`;}
// ==========================================// 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);
const response = await fetch(`https://${apiHost}${apiPath}`, { headers: { 'Authorization': token, 'Content-Type': 'application/x-www-form-urlencoded' } });
if (!response.ok) return [];
const data = await response.json(); if (!data.items) return [];
return data.items .filter(item => item.mimeType && item.mimeType.startsWith('image/')) .map(item => `${CONFIG.DOMAIN}/${item.key}`);
} catch (error) { console.error(`拉取目录 ${prefix} 失败:`, error); return []; }}
// ==========================================// 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" } });
if (response.ok) { // 拿到图片后,重写响应头,强行允许跨域 const newHeaders = new Headers(response.headers); newHeaders.set("Access-Control-Allow-Origin", "*"); newHeaders.set("Cache-Control", "no-cache"); // 告诉浏览器不要缓存当前API链接,以免下次拿不到新图
// 返回 200 状态码和真实图片数据流 return new Response(response.body, { status: 200, headers: newHeaders }); }
// 如果图片代理失败,降级回 302 重定向 return Response.redirect(imageUrl, 302); } catch (e) { // 网络异常,降级回 302 重定向 return Response.redirect(imageUrl, 302); }}
// ==========================================// 6. 主路由逻辑// ==========================================async function handleRequest(request) { const url = new URL(request.url); const route = url.pathname;
// 初始化或更新缓存 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) ]);
if (newPc.length > 0) pcImages = newPc; if (newMobile.length > 0) mobileImages = newMobile; lastCheckTime = Date.now(); } catch (e) { console.error("更新列表失败,暂时使用旧缓存"); } }
// --- 处理电脑壁纸 --- 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); }
// --- 处理手机壁纸 --- 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); }
return new Response("API 正常运行。电脑壁纸请访问 /api,手机壁纸请访问 /m-api", { status: 200, headers: { "Content-Type": "text/html;charset=UTF-8" } });}5.4 新增触发规则
- 函数发布完成后进入触发规则管理页面,新增自定义匹配规则;
- 匹配路径配置:
^/api.*、^/m-api.*,匹配域名选择已接入业务站点域名api.img.你的备案域名.com; - 保存规则,等待 2 ~ 5 min 完成全网边缘节点配置同步。

6 全维度功能测试验证
6.1 基础访问测试
- 请求
https://api.img.你的备案域名.com/api,多次刷新随机返回 PC 宽屏壁纸二进制图片;
- 请求
https://api.img.你的备案域名.com/m-api,随机返回移动端竖屏适配图片;
- 非法路由如
/test访问,接口返回 404 文本提示,路由匹配逻辑正常。
6.2 缓存机制测试
- 浏览器开发者工具查看响应头,携带
Cache-Control: public,max-age=86400缓存标识; - 同一区域重复访问同一张图片,EdgeOne 边缘节点命中本地缓存,无额外存储回源请求;
- 高频调用场景下,持续削减七牛云 / S3 对象存储下行流量,长期降低存储计费成本。
6.3 跨域测试
- 前端页面通过 fetch、axios 直接调用接口,浏览器无 CORS 跨域拦截报错;
- OPTIONS 预检请求返回 204 状态码,完整携带跨域允许头,兼容各类前端请求场景。
6.4 异常降级测试
- 目标图库目录未上传任何图片,接口自动返回配置兜底占位图;
- 存储 AK / SK 配置错误、存储接口网络超时,捕获异常后切换兜底图片;
- 存储 CDN 域名失效、图片资源被删除,统一走降级逻辑,页面无空白崩溃。
7 当前代码现存缺陷 & 可落地迭代优化方案
7.1 现有代码局限
- 内存单实例缓存短板:EdgeOne 边缘单实例内存缓存不持久,节点扩容、实例重启后缓存数据清空,无分布式持久化缓存支撑;
- 图片数量上限约束:七牛 list 接口单次限制返回 1000 条文件,图库图片总量超过 1000 张时无法全量随机抽取;
- 缺少图片尺寸过滤逻辑:仅通过文件后缀筛选资源,无法根据宽高比例筛选适配图片;
- 无接口限流防护:未增加 IP 访问频次限制,恶意高频刷接口会持续消耗存储回源流量;
- 日志体系简陋:仅基础控制台打印错误信息,无结构化日志上报,无法统计缓存命中率、接口访问量。
7.2 落地迭代优化方案
- 接入 EdgeOne KV 分布式缓存,将文件列表持久化缓存,减少重复请求存储列表接口;
- 递归分页拉取存储文件列表,突破单轮 1000 条文件上限,支持上万张图库全量随机;
- 扩展接口传参能力,支持前端传入宽高比例参数,过滤对应尺寸图片资源;
- 基于客户端 IP + KV 存储实现分钟级限流,拦截恶意高频刷接口行为;
- 对接 EdgeOne 日志服务,结构化记录缓存命中、回源失败、访问频次等运营数据;
- 增加动态缓存时长参数,前端可按需传入 max-age 控制边缘缓存有效期;
- 抽离签名逻辑,兼容七牛、COS、OSS、MinIO 多类 S3 标准存储一键切换。
8 全文总结
8.1 方案适用场景
- Hexo / VitePress 等纯静态博客、个人 / 企业静态站点,需要随机封面、随机壁纸 API;
- 业务需要 PC、移动端两套完全隔离的图片资源池,区分设备展示不同尺寸图片;
- 无后端服务器,希望依靠边缘计算实现轻量图片接口,降低服务器运维成本;
- 希望隐藏对象存储真实域名,减少源站流量消耗,控制存储服务计费。
8.2 整体工程优势
- 零后端服务部署,仅依赖 EdgeOne 边缘函数,服务器运维成本几乎为 0;
- 全球边缘节点分发图片资源,图片加载速度优于前端直连对象存储;
- 鉴权密钥存放边缘环境变量,前端无法获取存储地址与鉴权信息,数据安全性更高;
- 统一封装跨域、缓存、异常降级逻辑,一套代码兼容全部 S3 标准对象存储;
- 完整工程闭环:业务需求 → 方案对比 → 架构设计 → 源码拆解 → 部署实操 → 全维度测试 → 迭代优化,所有代码配置可直接复现落地。
Created with ❤️ by 十三
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
腾讯云 EdgeOne 边缘函数实现七牛云 & S3 存储随机图床 API
https://blog.nw177.cn/posts/40-博客搭建/07-腾讯云-edgeone-边缘函数实现七牛s3-随机图床-api/相关文章智能推荐
1
腾讯EdgeOne部署Twikoo评论系统指南
Blog腾讯EdgeOne部署Twikoo评论系统指南
2
赛博大善人 Cloudflare R2 图床
Blog基于 Cloudflare R2 + 腾讯 Edge One + PicList 搭建免费、全球加速的图床方案
3
白嫖免费图床,套 CDN 加速
Blog白嫖免费图床,套 CDN 加速
4
你是否需要一个图床?
docker你是否需要一个图床?
5
兰空图床和高清图标库
docker兰空图床和高清图标库
随机文章随机推荐





