缤纷云 S4 对象存储接入:从本地磁盘到云端的迁移实践
这篇文章记录了我把个人作品集网站的图片存储,从服务器本地磁盘迁移到缤纷云 S4 对象存储的全过程,包括架构设计、CDN 加速和高级防盗链配置。
为什么要把图片搬到云端?
迁移前,所有图片都存在服务器本地磁盘(public/uploads/):
- 服务器磁盘有限,照片、壁纸越存越多
- 图片直接由 Next.js 服务读取,占用服务器带宽
- 没有 CDN 加速,全国访客加载速度差异大
- 生产构建后还要处理
.next与public的同步问题
缤纷云 S4 兼容 S3 协议,自带 CDN 加速和高级防盗链,正好解决上面所有问题。
整体架构
[浏览器] ──POST /api/upload──→ [Next 服务端] ──生成预签名 Put URL──→ [缤纷云 S4]
│ │
│ └──返回 presigned URL + accessPath ─→ [浏览器直传 S4]
│
│ GET /api/s4/xxx.jpg
│ [Next 服务端]
│ ←──302 → location: https://cdn.domain/uploads/xxx.jpg?_ts=...&_btf_tk=...
│
[浏览器] ──直连缤纷云 CDN────→ 高速加载
核心设计思想:上传用预签名直传,读取用服务端签名跳转。
上传:预签名 URL 直传
服务端不直接接收文件,而是生成一个预签名 PutObject URL 发给浏览器,浏览器直接 PUT 到缤纷云。这样 AK/SK 永远不会暴露在前端。
// src/lib/s4.ts
export async function generatePresignedUploadUrl(key: string, expiresIn = 3600) {
const command = new PutObjectCommand({ Bucket: setting.bucket, Key: key });
return getSignedUrl(getS3Client(setting), command, { expiresIn });
}
读取:CDN 高级防盗链签名
浏览器请求 /api/s4/xxx.jpg,服务端生成一个短时签名 URL,302 跳转到 CDN 加载。
export function generateSignedCdnUrl(path: string, ttl?: number) {
const deadline = Math.floor(Date.now() / 1000) + (ttl ?? 3600);
const rawString = `${authKey}${path}${deadline}`;
const md5Result = crypto.createHash('md5').update(rawString).digest('hex');
return `${cdnDomain}${path}?_btf_tk=${md5Result}&_ts=${deadline}`;
}
安全设置效果
迁移完成后,在缤纷云控制台做了一系列安全加固:
1. CDN 高级防盗链(URL 鉴权)
- 请求必须带合法签名(
_btf_tk+_ts) - 无签名 → 403
- 签名过期 → 403
- 伪造签名 → 403
效果:即使有人拿到 CDN 直链,过一会儿就失效,无法长期盗用。
2. Referer 白名单
只允许本站域名引用图片:
*.luckymeet.vip
localhost
- 本站页面加载 → ✅ 放行
- 外部网站盗链
<img src="CDN URL">→ ❌ 拒绝
这个能挡住 99% 的随意盗链。注意:Referer 可以伪造,防君子不防小人,但普通用户浏览器不会伪造。
3. User-Agent 黑名单
封禁常见的爬虫和脚本 UA:
curl*
python*
Go-http-client*
wget*
java*
axios*
node-fetch*
okhttp*
scrapy*
4. 空 Referer / 空 User-Agent 不允许访问
正常浏览器请求一定有 Referer 和 UA,空的都是脚本或异常请求,直接拒绝。
几个容易混淆的概念
/api/s4/ 和桶里的 uploads/ 是两回事
s4是 API 路由名,不是桶里的文件夹- 桶里的 key 由
generateUniqueKey()决定,当前是uploads/xxx.jpg - API 路由把
s4/xxx.jpg映射到桶里的uploads/xxx.jpg
别人访问我的 localhost 无效
别人写 <img src="http://localhost:3000/api/s4/xxx.jpg">,访问的是他电脑上的 localhost,不是我的服务器,图片自然显示不出来。
CDN 的图片本就不需要 CORS
只要用 <img> 标签加载图片,就不需要配置 CORS。只有 JS 用 fetch 读取图片做 Canvas 处理时才需要。
总结
| 需求 | 方案 |
|---|---|
| AK/SK 不暴露 | 预签名直传,AK/SK 只在服务端 |
| 图片加速 | CDN 高速加载 |
| 防盗链 | CDN 高级鉴权 + Referer 白名单 + UA 黑名单 |
| 配置热更新 | 管理后台修改 S4 设置,无需重启 |