cover

迷途羔羊

DopaMine.

腾讯云 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全球边缘节点

路由匹配判断

读取PC图库目录 blog/bg/

读取移动端图库目录 blog/bg-m/

携带鉴权签名请求存储获取图片列表

随机选取单张图片资源

边缘回源拉取图片二进制流

追加跨域、缓存响应头

边缘本地缓存图片

加载兜底默认图片

/api 路由

/m-api 路由

请求异常/目录无图

用户浏览器

EdgeOne全球边缘节点

路由匹配判断

读取PC图库目录 blog/bg/

读取移动端图库目录 blog/bg-m/

携带鉴权签名请求存储获取图片列表

随机选取单张图片资源

边缘回源拉取图片二进制流

追加跨域、缓存响应头

边缘本地缓存图片

加载兜底默认图片

  • 用户发起 /api/m-api 接口请求,流量调度至就近 EdgeOne 边缘节点;
  • 边缘函数匹配请求路由绑定对应资源目录,携带鉴权签名调用存储文件列表接口;
  • 过滤图片格式后随机选取资源,边缘节点回源拉取图片二进制;
  • 统一拼接跨域、长缓存响应头,图片流返回客户端并在边缘本地缓存;
  • 链路任意环节触发异常时,自动加载兜底图片返回,保障页面正常渲染。

3.2 资源分层规则#

  • PC 端图库目录:blog/bg/,存放 16<9> / 21<9> 宽屏高清壁纸,适配桌面端展示;
  • 移动端图库目录:blog/bg-m/,存放 9<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 控制边缘缓存有效期;
  • 抽离签名逻辑,兼容七牛、COSOSS、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/
作者
十三
发布于
2026-07-13
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
十三
终不似,少年游。
📢 欢迎来访者
👋🏻 Hi,我是十三,欢迎来到我的博客!这里是分享知识、交流想法的地方。希望你能在这里找到有价值的内容!
分类
标签
站点统计
文章
55
分类
9
标签
58
总字数
87,993
运行时长
0
最后活动
0 天前
站点信息
构建平台
EdgeOne
博客版本
Firefly v6.13.9
文章许可
CC BY-NC-SA 4.0
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
--%
本年还剩 -- 天
--%
本月还剩 -- 天
--%
本周还剩 -- 天

距离 --

--

--

我和宝宝在一起已经
---------TSH❤️CXY---------
---------TSH
❤️
CXY---------
0000000
✨ 今日一言
"山重水复疑无路,柳暗花明又一村。"
—— 陆游
天气预报
统计
✨️ 复制成功,转载请标注本文地址