feat: enhance demo components with consistent styling and info boxes

- Add standardized header and info box components to all demo files
- Improve visual consistency with theme colors and spacing
- Add max-height and overflow-y for better content containment
- Update package.json build script with --force flag
- Add .gitignore entries for REFACTORING files
- Fix table formatting in audio-intro.md
This commit is contained in:
sanbuphy
2026-02-14 12:14:07 +08:00
parent cd2ce9e661
commit ebe2bf6109
70 changed files with 12307 additions and 10445 deletions
+212 -122
View File
@@ -31,13 +31,13 @@
**核心区别一览**
| 维度 | 传统文件系统 | 对象存储 |
| :--- | :--- | :--- |
| **组织方式** | 层级目录树 | 扁平键值对 |
| **访问协议** | POSIX(本地文件操作) | HTTP/REST API |
| **扩展性** | 单机容量有限 | 近乎无限水平扩展 |
| **元数据** | 基础属性(大小、时间) | 丰富的自定义元数据 |
| **典型场景** | 本地办公文档 | 图片/视频/备份/静态资源 |
| 维度 | 传统文件系统 | 对象存储 |
| :----------- | :--------------------- | :---------------------- |
| **组织方式** | 层级目录树 | 扁平键值对 |
| **访问协议** | POSIX(本地文件操作) | HTTP/REST API |
| **扩展性** | 单机容量有限 | 近乎无限水平扩展 |
| **元数据** | 基础属性(大小、时间) | 丰富的自定义元数据 |
| **典型场景** | 本地办公文档 | 图片/视频/备份/静态资源 |
### 1.2 对象存储的核心概念
@@ -46,6 +46,7 @@
桶是对象存储的顶级容器,相当于一个独立的命名空间。所有对象都必须存放在某个桶中。
**命名规则**(以阿里云 OSS 为例):
- 全局唯一:在整个云厂商的所有用户中不能重复
- 只能包含小写字母、数字和短横线
- 必须以小写字母或数字开头和结尾
@@ -73,11 +74,11 @@
对象存储提供多层权限控制:
| 层级 | 控制方式 | 典型场景 |
| :--- | :--- | :--- |
| **桶级别** | Bucket Policy(资源策略) | 禁止所有外网访问、只允许特定 IP |
| **对象级别** | ACL(访问控制列表) | 公开图片、私有文档 |
| **临时授权** | STS(安全令牌服务) | 前端直传、移动端上传 |
| 层级 | 控制方式 | 典型场景 |
| :----------- | :------------------------ | :------------------------------ |
| **桶级别** | Bucket Policy(资源策略) | 禁止所有外网访问、只允许特定 IP |
| **对象级别** | ACL(访问控制列表) | 公开图片、私有文档 |
| **临时授权** | STS(安全令牌服务) | 前端直传、移动端上传 |
**安全红线**:永远不要把 AccessKey ID 和 AccessKey Secret 写在前端代码里!正确做法是:前端向你的后端申请临时 STS 凭证,后端验证身份后返回带过期时间的临时凭证。
@@ -102,11 +103,13 @@
#### 边缘节点:离用户最近的"快递站"
边缘节点是 CDN 网络中最接近用户的层级,通常部署在:
- 运营商机房(联通/电信/移动)
- 大城市互联网交换中心
- 重要交通枢纽
**中国主要 CDN 节点分布**
- 一线城市:北京、上海、广州、深圳
- 二线城市:杭州、南京、成都、武汉、西安
- 海外:香港、新加坡、东京、硅谷、法兰克福
@@ -116,11 +119,13 @@
#### 源站:内容的"总仓库"
源站是 CDN 回源获取内容的地方,可以是:
- 对象存储(OSS/COS/S3
- 自建服务器(ECS/物理机)
- 负载均衡(SLB/CLB
**关键配置**
- **回源 HOST**:CDN 节点访问源站时使用的域名/IP
- **回源协议**HTTP 还是 HTTPS
- **回源端口**:80、443 还是自定义端口
@@ -128,10 +133,12 @@
#### 中间层节点:"区域分拨中心"
在边缘节点和源站之间,CDN 通常还有一层或多层中间节点:
- **汇聚节点**:聚合多个边缘节点的回源请求,减少源站压力
- **区域中心**:负责一个大区的内容分发和调度
这种分层架构的好处:
1. **降低源站压力**:1000 个边缘节点的请求,可能只需要向源站发起 10 次
2. **提高命中率**:热门内容在中间层就被拦截,不需要回源
3. **故障隔离**:某条链路出问题,可以自动切换到其他路径
@@ -143,14 +150,17 @@
<CachePolicyDemo />
**Step 1DNS 解析**(智能调度)
```
用户输入:cdn.example.com/image.jpg
DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
```
这里的关键是**智能 DNS**:根据用户的运营商、地理位置、节点负载,返回最优的 CDN 节点 IP。
**Step 2:边缘节点查找**(缓存命中?)
```
请求到达北京联通 CDN 节点(1.2.3.4
@@ -160,6 +170,7 @@ DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
```
**Step 3:回源获取**(层层向上)
```
边缘节点未命中
@@ -173,6 +184,7 @@ DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
```
**Step 4:缓存并返回**(下次更快)
```
内容沿链路返回
@@ -198,17 +210,20 @@ DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
```
**流程**
1. 用户选择文件,点击上传
2. 文件先上传到你的后端服务器
3. 后端接收完整文件后,再转上传到对象存储
4. 返回上传结果给用户
**优点**
- 实现简单,前后端都好控制
- 可以在后端做文件校验、格式转换
- 敏感操作可以记录日志、做权限校验
**缺点**
- **带宽双吃**:用户上传占用一次带宽,服务器转传又占用一次
- **服务器压力大**:大文件会占用大量内存和 CPU
- **上传慢**:相当于多了一道中转,用户感知到的上传时间更长
@@ -224,6 +239,7 @@ DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
```
**流程**
1. 用户选择文件,前端先向后端申请"上传凭证"
2. 后端验证用户身份,向对象存储服务申请**临时 STS 凭证**(带过期时间)
3. 后端把临时凭证返回给前端
@@ -231,12 +247,14 @@ DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
5. 对象存储返回上传结果,前端通知后端"上传完成"
**优点**
- **上传快**:少了中转环节,用户感知速度最快
- **服务器压力小**:只处理凭证签发,不处理文件流
- **带宽省**:只走一次上传流量
- **安全性高**:临时凭证有过期时间,泄露也危害有限
**缺点**
- 实现稍复杂,需要理解 STS、签名机制
- 前端需要处理分片上传、断点续传等逻辑
- 跨域(CORS)需要配置
@@ -261,21 +279,21 @@ DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
**为什么需要分片?**
| 场景 | 不分片 | 分片 |
| :--- | :--- | :--- |
| **网络波动** | 传了 99% 断网,全部重传 | 只重传失败的分片 |
| **上传速度** | 单线程,速度慢 | 多线程并行,速度快 |
| **内存占用** | 需要缓存整个文件 | 只需缓存当前分片 |
| **进度显示** | 只有 0% 和 100% | 精确到每个分片的进度 |
| 场景 | 不分片 | 分片 |
| :----------- | :---------------------- | :------------------- |
| **网络波动** | 传了 99% 断网,全部重传 | 只重传失败的分片 |
| **上传速度** | 单线程,速度慢 | 多线程并行,速度快 |
| **内存占用** | 需要缓存整个文件 | 只需缓存当前分片 |
| **进度显示** | 只有 0% 和 100% | 精确到每个分片的进度 |
**主流云厂商的分片规格**
| 厂商 | 分片大小限制 | 最大分片数 | 最小分片大小 |
| :--- | :--- | :--- | :--- |
| **阿里云 OSS** | 100MB | 10000 | 100KB |
| **腾讯云 COS** | 5GB | 10000 | 1MB |
| **AWS S3** | 5GB | 10000 | 5MB(推荐) |
| **七牛云** | 100MB | 10000 | 4MB |
| 厂商 | 分片大小限制 | 最大分片数 | 最小分片大小 |
| :------------- | :----------- | :--------- | :----------- |
| **阿里云 OSS** | 100MB | 10000 | 100KB |
| **腾讯云 COS** | 5GB | 10000 | 1MB |
| **AWS S3** | 5GB | 10000 | 5MB(推荐) |
| **七牛云** | 100MB | 10000 | 4MB |
### 3.2 CDN 回源策略详解
@@ -284,6 +302,7 @@ DNS 服务器返回:北京联通 CDN 节点 IP(1.2.3.4
#### 什么是"回源"
CDN 边缘节点缓存了源站的内容,但当:
- 用户请求的内容**第一次被访问**
- 缓存的内容**已过期(TTL 到期)**
- 缓存被**手动刷新/预热**
@@ -292,11 +311,11 @@ CDN 节点就需要向**源站**请求最新内容,这个过程就叫"回源"
#### 回源的三种模式
| 模式 | 原理 | 适用场景 | 优缺点 |
| :--- | :--- | :--- | :--- |
| **直接回源** | CDN 节点 → 源站 | 源站有公网 IP,且流量不大 | 简单直接,但源站压力大 |
| **中间源回源** | CDN 节点 → 中间层 → 源站 | 大型网站,多层缓存架构 | 分担源站压力,架构复杂 |
| ** OSS/COS 作为源站** | CDN 节点 → 对象存储 | 静态资源、图片、视频 | 最佳实践,成本低、性能好 |
| 模式 | 原理 | 适用场景 | 优缺点 |
| :-------------------- | :----------------------- | :------------------------ | :----------------------- |
| **直接回源** | CDN 节点 → 源站 | 源站有公网 IP,且流量不大 | 简单直接,但源站压力大 |
| **中间源回源** | CDN 节点 → 中间层 → 源站 | 大型网站,多层缓存架构 | 分担源站压力,架构复杂 |
| ** OSS/COS 作为源站** | CDN 节点 → 对象存储 | 静态资源、图片、视频 | 最佳实践,成本低、性能好 |
#### 回源配置实战
@@ -315,6 +334,7 @@ CDN 节点就需要向**源站**请求最新内容,这个过程就叫"回源"
```
关键配置项:
- **源站类型**:OSS/COS 域名 或 自定义源站
- **回源协议**HTTP 还是 HTTPS(建议 HTTPS
- **回源 HOST**:访问源站时使用的 Host 头
@@ -332,6 +352,7 @@ CDN 边缘节点
```
主备模式:
```
CDN 边缘节点
├─ 主源站 A (健康时全部流量)
@@ -342,12 +363,13 @@ CDN 边缘节点
这里有个容易混淆的概念:
| 指标 | 定义 | 计费关系 |
| :--- | :--- | :--- |
| **CDN 下行带宽** | 从 CDN 节点到用户的流量 | 通常按流量计费的 CDN 费用 |
| **回源带宽** | 从源站到 CDN 节点的流量 | 通常对象存储或源站出流量费用 |
| 指标 | 定义 | 计费关系 |
| :--------------- | :---------------------- | :--------------------------- |
| **CDN 下行带宽** | 从 CDN 节点到用户的流量 | 通常按流量计费的 CDN 费用 |
| **回源带宽** | 从源站到 CDN 节点的流量 | 通常对象存储或源站出流量费用 |
**省钱技巧**
- 提高 CDN 命中率(让更多请求命中缓存,减少回源)
- 设置合理的缓存时间(TTL
- 使用预热功能,在用户访问前就缓存热点内容
@@ -362,10 +384,12 @@ CDN 边缘节点
CDN 如何判断两次请求是否应该返回同一个缓存副本?靠的就是**缓存键**。
**默认缓存键通常包括**
- URL 路径(不含查询参数)
- 例如:`/images/photo.jpg`
**问题场景**
```
用户 A 请求:/images/photo.jpg?w=100&h=100 (100x100 缩略图)
用户 B 请求:/images/photo.jpg?w=800&h=600 (800x600 大图)
@@ -375,14 +399,15 @@ CDN 如何判断两次请求是否应该返回同一个缓存副本?靠的就
**解决方案:自定义缓存键规则**
| 规则 | 示例 | 效果 |
| :--- | :--- | :--- |
| **保留指定查询参数** | 保留 `w``h` | 不同尺寸分别缓存 |
| **保留所有查询参数** | 保留全部 | 完全精确匹配 |
| 规则 | 示例 | 效果 |
| :------------------- | :------------------------ | :------------------------ |
| **保留指定查询参数** | 保留 `w``h` | 不同尺寸分别缓存 |
| **保留所有查询参数** | 保留全部 | 完全精确匹配 |
| **忽略特定查询参数** | 忽略 `token``timestamp` | 带时间戳的 URL 能命中缓存 |
| **包含请求头** | 包含 `Accept-Language` | 不同语言返回不同内容 |
| **包含请求头** | 包含 `Accept-Language` | 不同语言返回不同内容 |
**实战配置示例**(阿里云 CDN):
```
缓存键规则:
- URL 路径:/images/*
@@ -396,13 +421,13 @@ TTLTime To Live)决定了内容在 CDN 节点上缓存多久。设置太短
**按文件类型设置 TTL 的建议**
| 文件类型 | 建议 TTL | 原因 |
| :--- | :--- | :--- |
| HTML 页面 | 0-5 分钟 | 内容频繁更新,需要实时 |
| 文件类型 | 建议 TTL | 原因 |
| :---------- | :---------------------- | :----------------------------- |
| HTML 页面 | 0-5 分钟 | 内容频繁更新,需要实时 |
| JS/CSS 文件 | 1 年(配合文件名 hash) | 内容不变,文件名变化即缓存失效 |
| 图片/视频 | 7-30 天 | 更新频率低,可长期缓存 |
| 字体文件 | 1 年 | 几乎不变 |
| API 响应 | 0-5 分钟(视业务) | 数据实时性要求高 |
| 图片/视频 | 7-30 天 | 更新频率低,可长期缓存 |
| 字体文件 | 1 年 | 几乎不变 |
| API 响应 | 0-5 分钟(视业务) | 数据实时性要求高 |
**前端工程化配合 CDN 的最佳实践**
@@ -425,11 +450,11 @@ output: {
当你更新了源站内容,但 CDN 缓存还没过期,用户看到的还是旧内容:
| 刷新类型 | 效果 | 耗时 | 适用场景 |
| :--- | :--- | :--- | :--- |
| **URL 刷新** | 指定 URL 的缓存失效 | 5-10 分钟 | 单个文件更新 |
| **目录刷新** | 指定目录下所有内容失效 | 10-30 分钟 | 批量更新 |
| **全站刷新** | 整个域名的缓存全部失效 | 30 分钟以上 | 紧急回滚 |
| 刷新类型 | 效果 | 耗时 | 适用场景 |
| :----------- | :--------------------- | :---------- | :----------- |
| **URL 刷新** | 指定 URL 的缓存失效 | 5-10 分钟 | 单个文件更新 |
| **目录刷新** | 指定目录下所有内容失效 | 10-30 分钟 | 批量更新 |
| **全站刷新** | 整个域名的缓存全部失效 | 30 分钟以上 | 紧急回滚 |
**重要提醒**:刷新只是让缓存失效,下次请求会回源拉取新内容。不要在高峰期大批量刷新,否则可能导致源站被打爆。
@@ -458,12 +483,14 @@ output: {
### 4.1 智能 DNS 调度
传统 DNS 解析:
```
用户问:cdn.example.com 的 IP 是什么?
DNS 答:1.2.3.4(固定的)
```
智能 DNS 解析:
```
用户(北京联通)问:cdn.example.com 的 IP 是什么?
智能 DNS:让我查查... 北京联通的 CDN 节点是 1.2.3.4
@@ -486,6 +513,7 @@ DNS 答:1.2.3.4(固定的)
传统 DNS 有个问题:**DNS 劫持和解析延迟**。
**HTTP DNS 方案**
```
客户端 → 绕过系统 DNS → 直接问 HTTP DNS 服务(如 223.5.5.5:80
@@ -495,11 +523,13 @@ DNS 答:1.2.3.4(固定的)
```
优势:
- 防劫持:不走运营商 DNS
- 更精准:可以按客户端网络质量选择 IP
- 实时性:故障切换更快
**实战建议**
- 移动端 APP 强烈建议接入 HTTP DNS
- Web 端可以使用 CDN 提供的 CNAME 调度
- 关键业务可以做多 IP 容灾(一个域名返回多个 IP)
@@ -513,6 +543,7 @@ DNS 答:1.2.3.4(固定的)
### 5.1 为什么 CDN 上 HTTPS 很重要?
**场景对比**
```
无 HTTPS
用户访问 http://cdn.example.com/image.jpg
@@ -539,14 +570,15 @@ HTTP/2 多路复用生效
#### 证书管理
| 方案 | 说明 | 成本 | 适用场景 |
| :--- | :--- | :--- | :--- |
| **云厂商免费证书** | 阿里云/腾讯云等提供 | 免费 | 单域名,快速上手 |
| **Let's Encrypt** | 社区免费证书 | 免费 | 自动化部署 |
| 方案 | 说明 | 成本 | 适用场景 |
| :--------------------- | :-------------------- | :------------- | :--------------- |
| **云厂商免费证书** | 阿里云/腾讯云等提供 | 免费 | 单域名,快速上手 |
| **Let's Encrypt** | 社区免费证书 | 免费 | 自动化部署 |
| **商业 DV/OV/EV 证书** | 赛门铁克、GeoTrust 等 | ¥几百-几万/年 | 企业级、需要绿条 |
| **泛域名证书** | *.example.com | ¥几千/年 | 多子域名 |
| **泛域名证书** | \*.example.com | ¥几千/年 | 多子域名 |
**实战建议**
- 测试环境:Let's Encrypt 或云厂商免费证书
- 生产环境:泛域名证书(省事)或单域名 OV 证书(省钱)
- 注意证书过期时间,设置自动续期提醒
@@ -554,18 +586,21 @@ HTTP/2 多路复用生效
#### HTTPS 优化配置
**TLS 版本选择**
```
推荐配置:仅 TLS 1.2 和 TLS 1.3
兼容配置:TLS 1.1 + TLS 1.2 + TLS 1.3(兼容老旧浏览器)
```
**密码套件**
```
推荐:ECDHE 密钥交换 + AES-GCM 加密
禁用:DES、RC4、MD5、SHA1
```
**OCSP Stapling**
```
功能:CDN 节点预获取证书吊销状态
效果:减少客户端验证时间 200-500ms
@@ -573,6 +608,7 @@ HTTP/2 多路复用生效
```
**TLS 会话复用**
```
Session ID 复用:客户端带着上次 Session ID,服务端恢复会话
Session Ticket 复用:服务端把会话状态加密发给客户端,下次带来
@@ -582,6 +618,7 @@ Session Ticket 复用:服务端把会话状态加密发给客户端,下次
### 5.3 HTTP/2 与 HTTP/3 在 CDN 上的应用
**HTTP/2 多路复用**
```
HTTP/1.1
请求 1 (index.html) ────────────────→
@@ -603,6 +640,7 @@ HTTP/2
```
**HTTP/2 服务端推送**
```
场景:用户请求 index.html,里面引用了 style.css 和 script.js
@@ -620,6 +658,7 @@ HTTP/2 推送:
```
**HTTP/3 (QUIC)**
```
HTTP/2 的问题:基于 TCP,队头阻塞
→ 一个 TCP 包丢失,整个连接等待重传
@@ -654,6 +693,7 @@ CDN 带宽 = 所有边缘节点的出流量总和
```
**带宽与流量的关系**
```
1 Mbps 带宽持续跑 1 小时 = 450 MB 流量
(计算:1,000,000 bps × 3600s ÷ 8 ÷ 1024 ÷ 1024 ≈ 429 MB
@@ -689,13 +729,13 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
**命中率低的常见原因**
| 原因 | 现象 | 解决方案 |
| :--- | :--- | :--- |
| 缓存时间太短 | TTL 只有几分钟 | 根据文件类型调整 TTL |
| 查询参数变化 | URL 带随机数 | 配置忽略特定参数 |
| 缓存键设置不当 | 不该区分的被区分了 | 优化缓存键规则 |
| 内容更新频繁 | 文件经常被覆盖 | 使用版本号或 hash 文件名 |
| 首次访问多 | 新内容或新节点 | 提前预热 |
| 原因 | 现象 | 解决方案 |
| :------------- | :----------------- | :----------------------- |
| 缓存时间太短 | TTL 只有几分钟 | 根据文件类型调整 TTL |
| 查询参数变化 | URL 带随机数 | 配置忽略特定参数 |
| 缓存键设置不当 | 不该区分的被区分了 | 优化缓存键规则 |
| 内容更新频繁 | 文件经常被覆盖 | 使用版本号或 hash 文件名 |
| 首次访问多 | 新内容或新节点 | 提前预热 |
### 6.2 日志分析与问题排查
@@ -712,18 +752,19 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
关键字段解释:
| 字段 | 说明 | 分析价值 |
| :--- | :--- | :--- |
| `cache_status` | 缓存状态 | HIT(命中)、MISS(未命中)、EXPIRED(过期) |
| `response_time` | 响应时间(ms) | 判断用户体验,>500ms 需优化 |
| `http_status` | HTTP 状态码 | 404/500 错误排查 |
| `bytes_sent` | 发送字节数 | 带宽统计 |
| 字段 | 说明 | 分析价值 |
| :-------------- | :------------- | :------------------------------------------- |
| `cache_status` | 缓存状态 | HIT(命中)、MISS(未命中)、EXPIRED(过期) |
| `response_time` | 响应时间(ms) | 判断用户体验,>500ms 需优化 |
| `http_status` | HTTP 状态码 | 404/500 错误排查 |
| `bytes_sent` | 发送字节数 | 带宽统计 |
#### 常见问题排查
**问题 1:用户反映访问慢**
排查步骤:
```
1. 看日志 response_time
- 如果很大(>500ms):检查是缓存 MISS 还是源站慢
@@ -739,6 +780,7 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
**问题 2:缓存不生效,每次都回源**
排查清单:
```
□ 源站响应头是否有 Cache-Control: no-cache / private
□ URL 是否带随机参数(如 ?_=123456)?
@@ -750,6 +792,7 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
**问题 3:费用暴涨**
排查方向:
```
1. 看账单明细
- CDN 流量费高:检查是否有大文件被频繁访问,或被盗链
@@ -848,6 +891,7 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
#### 对象存储配置
**存储桶规划**
```
Bucket: myapp-images-prod
├─ 目录结构:
@@ -872,6 +916,7 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
```
**CORS 跨域配置**
```xml
<CORSConfiguration>
<CORSRule>
@@ -890,6 +935,7 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
#### CDN 加速配置
**缓存策略配置**
```
全局默认规则:
├─ 缓存键:URL 路径 + 保留 w、h、format 查询参数
@@ -916,6 +962,7 @@ CDN QPS = 所有边缘节点每秒处理的 HTTP 请求总数
```
**HTTPS 优化配置**
```
证书配置:
├─ 证书类型:泛域名证书 *.myapp.com
@@ -1031,14 +1078,14 @@ rules:
```yaml
# 带宽封顶配置
bandwidth_cap:
daily_limit: 500 # Mbps,日峰值超过则自动停用 CDN
monthly_limit: 10000 # GB,月流量超过则停用
daily_limit: 500 # Mbps,日峰值超过则自动停用 CDN
monthly_limit: 10000 # GB,月流量超过则停用
# 告警阈值
alerts:
- threshold: 70% # 达到 70% 发告警
- threshold: 70% # 达到 70% 发告警
channels: [sms, email]
- threshold: 90% # 达到 90% 打电话
- threshold: 90% # 达到 90% 打电话
channels: [phone]
```
@@ -1049,12 +1096,14 @@ bandwidth_cap:
### 8.1 架构设计原则
**原则 1:动静分离**
```
动态内容(API、HTML)→ 走源站或边缘函数
静态内容(图片、JS、CSS、视频)→ 走 CDN + 对象存储
```
**原则 2:就近服务**
```
用户在哪里,内容就缓存到哪里
→ 选择覆盖广的 CDN 服务商
@@ -1063,6 +1112,7 @@ bandwidth_cap:
```
**原则 3:分层缓存**
```
浏览器本地缓存(最强)
@@ -1074,6 +1124,7 @@ CDN 中间层/区域节点(兜底)
```
**原则 4:成本与体验的平衡**
```
存储分级:热数据标准存储,冷数据归档存储
缓存策略:高频内容长 TTL,低频内容短 TTL
@@ -1084,24 +1135,28 @@ CDN 中间层/区域节点(兜底)
### 8.2 避坑清单
**存储桶命名与权限**
- [ ] 桶名全局唯一,避免被占用
- [ ] 私有文件不要设置为公共读
- [ ] AccessKey 不要写在前端代码里,用 STS 临时凭证
- [ ] 启用服务端加密(SSE)保护敏感数据
**CDN 缓存配置**
- [ ] HTML 文件 TTL 不要太长(建议 < 5 分钟)
- [ ] JS/CSS 建议用带 hash 的文件名,TTL 设为 1 年
- [ ] 缓存键要合理,不要把用户信息等变量放进去
- [ ] 重要更新后记得刷新缓存或预热
**HTTPS 安全**
- [ ] 证书不要过期,设置自动续期
- [ ] 最低 TLS 版本建议 1.2
- [ ] 开启 HSTS 防止降级攻击
- [ ] 敏感 Cookie 设置 Secure 和 HttpOnly
**成本控制**
- [ ] 开启带宽封顶告警,防止异常流量
- [ ] 低频/归档存储有最小存储时间和提前删除费,注意规则
- [ ] 回源流量费也很贵,努力提高 CDN 命中率
@@ -1170,7 +1225,12 @@ class DirectUploader {
const formData = new FormData()
// 构建表单字段(不同厂商字段名不同)
const formFields = this._buildFormFields(credentials, fileKey, file.type, options)
const formFields = this._buildFormFields(
credentials,
fileKey,
file.type,
options
)
Object.entries(formFields).forEach(([key, value]) => {
formData.append(key, value)
})
@@ -1210,7 +1270,11 @@ class DirectUploader {
const fileKey = this._generateFileKey(file, options.directory)
// 1. 初始化分片上传
const uploadId = await this._initMultipartUpload(credentials, fileKey, file.type)
const uploadId = await this._initMultipartUpload(
credentials,
fileKey,
file.type
)
// 2. 计算分片
const parts = []
@@ -1232,21 +1296,30 @@ class DirectUploader {
// 支持断点续传:检查哪些分片已上传
if (options.resume) {
const existingParts = await this._listParts(credentials, fileKey, uploadId)
const existingParts = await this._listParts(
credentials,
fileKey,
uploadId
)
for (const part of existingParts) {
uploadedParts.push(part)
}
}
// 过滤出未上传的分片
const pendingParts = parts.filter(p =>
!uploadedParts.some(up => up.partNumber === p.number)
const pendingParts = parts.filter(
(p) => !uploadedParts.some((up) => up.partNumber === p.number)
)
// 并发上传
const uploadPart = async (part) => {
try {
const etag = await this._uploadPart(credentials, fileKey, uploadId, part)
const etag = await this._uploadPart(
credentials,
fileKey,
uploadId,
part
)
return { partNumber: part.number, etag }
} catch (error) {
failedParts.push({ part, error })
@@ -1271,11 +1344,18 @@ class DirectUploader {
// 检查是否所有分片都上传成功
if (uploadedParts.length !== totalParts) {
throw new Error(`Upload incomplete: ${uploadedParts.length}/${totalParts} parts uploaded`)
throw new Error(
`Upload incomplete: ${uploadedParts.length}/${totalParts} parts uploaded`
)
}
// 4. 完成分片上传(合并分片)
await this._completeMultipartUpload(credentials, fileKey, uploadId, uploadedParts)
await this._completeMultipartUpload(
credentials,
fileKey,
uploadId,
uploadedParts
)
return {
url: this._getFileUrl(fileKey),
@@ -1316,7 +1396,11 @@ class DirectUploader {
_getFileUrl(key) {
return `https://${this.bucket}.${this.provider === 'oss' ? 'oss' : 'cos'}-${this.region}.${
this.provider === 'oss' ? 'aliyuncs.com' : this.provider === 'cos' ? 'myqcloud.com' : 'amazonaws.com'
this.provider === 'oss'
? 'aliyuncs.com'
: this.provider === 'cos'
? 'myqcloud.com'
: 'amazonaws.com'
}/${key}`
}
@@ -1385,7 +1469,9 @@ async function uploadVideo(file) {
parallel: 3, // 3 个并发
resume: true, // 支持断点续传
onProgress: (progress) => {
console.log(`上传进度: ${progress.percent}%, 已传 ${progress.loaded}/${progress.total}`)
console.log(
`上传进度: ${progress.percent}%, 已传 ${progress.loaded}/${progress.total}`
)
},
onPartComplete: (part) => {
console.log(`分片 ${part.number} 上传完成`)
@@ -1464,9 +1550,7 @@ router.post('/credentials', async (req, res) => {
'oss:AbortMultipartUpload',
'oss:ListParts'
],
Resource: [
`acs:oss:*:*:${config.oss.bucket}/${prefix}*`
]
Resource: [`acs:oss:*:*:${config.oss.bucket}/${prefix}*`]
}
],
Version: '1'
@@ -1495,7 +1579,13 @@ router.post('/credentials', async (req, res) => {
prefix: prefix, // 文件路径前缀
// 安全限制
maxSize: 100 * 1024 * 1024, // 最大 100MB
allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4']
allowedTypes: [
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'video/mp4'
]
}
}
})
@@ -1565,27 +1655,27 @@ module.exports = router
const refererConfig = {
// 白名单模式:只允许以下 Referer 访问
allowList: [
'*.myapp.com', // 主站
'*.myapp.cn', // 国内站
'localhost:*', // 本地开发
'*.myapp.com', // 主站
'*.myapp.cn', // 国内站
'localhost:*', // 本地开发
'127.0.0.1:*'
],
// 黑名单模式(可选):禁止以下 Referer
blockList: [
'*. competitor.com', // 竞争对手
'*. competitor.com', // 竞争对手
'spam-site.com'
],
// 空 Referer 处理:是否允许直接访问(浏览器地址栏输入 URL)
allowEmptyReferer: false // 生产环境建议 false,测试环境可 true
allowEmptyReferer: false // 生产环境建议 false,测试环境可 true
}
// 2. URL 鉴权(更安全的防盗链,带时间戳和签名)
class URLAuth {
constructor(config) {
this.key = config.key // 鉴权密钥,只在服务端保存
this.expireTime = config.expireTime || 3600 // 默认 1 小时有效期
this.key = config.key // 鉴权密钥,只在服务端保存
this.expireTime = config.expireTime || 3600 // 默认 1 小时有效期
}
/**
@@ -1645,11 +1735,14 @@ class URLAuth {
// 使用示例
const auth = new URLAuth({
key: 'your-secret-key-only-known-by-server',
expireTime: 3600 // 1 小时有效期
expireTime: 3600 // 1 小时有效期
})
// 服务端生成带签名的 URL
const signedUrl = auth.sign('https://cdn.example.com/private/document.pdf', 7200)
const signedUrl = auth.sign(
'https://cdn.example.com/private/document.pdf',
7200
)
// 结果:https://cdn.example.com/private/document.pdf?sign=xxxxx&t=1699123456
// CDN 边缘或源站验证
@@ -1662,15 +1755,12 @@ if (!result.valid) {
const ipConfig = {
// 只允许特定 IP 访问(适合内部系统)
whiteList: [
'192.168.1.0/24', // 内网网段
'192.168.1.0/24', // 内网网段
'10.0.0.0/8'
],
// 禁止特定 IP 访问(封禁攻击者)
blackList: [
'1.2.3.4',
'5.6.7.8'
]
blackList: ['1.2.3.4', '5.6.7.8']
}
// 4. UAUser-Agent)黑白名单
@@ -1687,7 +1777,7 @@ const uaConfig = {
// 只允许浏览器访问(严格模式)
whiteList: [
'Mozilla/*', // 现代浏览器
'Mozilla/*', // 现代浏览器
'AppleWebKit/*'
]
}
@@ -1697,28 +1787,28 @@ const uaConfig = {
## 10. 名词对照表
| 英文术语 | 中文对照 | 解释 |
| :--- | :--- | :--- |
| **Object Storage** | 对象存储 | 一种数据存储架构,将数据作为对象管理,而非文件系统层级结构。适合存储图片、视频、备份等非结构化数据。 |
| **Bucket** | 存储桶 | 对象存储中的顶级容器,用于组织和隔离数据。每个桶有独立的权限控制和配置。 |
| **Object** | 对象/文件对象 | 对象存储的基本单元,包含数据本身、元数据(Metadata)和全局唯一键(Key)。 |
| **CDN** | 内容分发网络 | Content Delivery Network,通过在全球部署边缘节点,将网站内容缓存到离用户最近的位置,加速访问速度。 |
| **Edge Node** | 边缘节点 | CDN 网络中部署在各地的缓存服务器,直接为用户提供内容访问服务。 |
| **Origin** | 源站 | CDN 回源获取内容的服务器,可以是对象存储、ECS 或自建服务器。 |
| **Cache Hit** | 缓存命中 | 用户请求的内容在 CDN 边缘节点已存在,直接返回,无需回源。 |
| **Cache Miss** | 缓存未命中 | 边缘节点没有请求的内容,需要回源获取。 |
| **Hit Ratio** | 命中率 | 缓存命中次数占总请求次数的比例。命中率越高,回源越少,成本越低。 |
| **TTL** | 生存时间/缓存时间 | Time To Live,内容在 CDN 节点上缓存的有效期。过期后需要重新回源。 |
| **Back to Source** | 回源 | CDN 边缘节点向源站请求内容的过程。 |
| **Purge/Refresh** | 刷新缓存 | 强制使 CDN 缓存失效,下次请求回源获取最新内容。 |
| **Preheat** | 预热 | 在正式发布前,主动将内容推送到 CDN 节点,让用户第一次访问就能命中缓存。 |
| **CORS** | 跨域资源共享 | Cross-Origin Resource Sharing,浏览器的安全机制,控制不同域之间的资源访问。 |
| **Referer** | 来源页面 | HTTP 请求头字段,指示请求是从哪个页面链接过来的。用于防盗链。 |
| **STS** | 安全令牌服务 | Security Token Service,颁发临时访问凭证的服务,用于前端直传等场景。 |
| **Multipart Upload** | 分片上传 | 将大文件切分成多个小分片并行上传,支持断点续传,提高上传效率和可靠性。 |
| **ETag** | 实体标签 | HTTP 响应头,用于标识资源的特定版本,常用于缓存验证。 |
| **S3 API** | S3 兼容接口 | AWS S3 的对象存储 API 规范,多数云厂商的对象存储都兼容此接口。 |
| **Canonical Query String** | 规范查询字符串 | 签名字符串的一部分,用于计算请求签名,确保请求不被篡改。 |
| 英文术语 | 中文对照 | 解释 |
| :------------------------- | :---------------- | :--------------------------------------------------------------------------------------------------- |
| **Object Storage** | 对象存储 | 一种数据存储架构,将数据作为对象管理,而非文件系统层级结构。适合存储图片、视频、备份等非结构化数据。 |
| **Bucket** | 存储桶 | 对象存储中的顶级容器,用于组织和隔离数据。每个桶有独立的权限控制和配置。 |
| **Object** | 对象/文件对象 | 对象存储的基本单元,包含数据本身、元数据(Metadata)和全局唯一键(Key)。 |
| **CDN** | 内容分发网络 | Content Delivery Network,通过在全球部署边缘节点,将网站内容缓存到离用户最近的位置,加速访问速度。 |
| **Edge Node** | 边缘节点 | CDN 网络中部署在各地的缓存服务器,直接为用户提供内容访问服务。 |
| **Origin** | 源站 | CDN 回源获取内容的服务器,可以是对象存储、ECS 或自建服务器。 |
| **Cache Hit** | 缓存命中 | 用户请求的内容在 CDN 边缘节点已存在,直接返回,无需回源。 |
| **Cache Miss** | 缓存未命中 | 边缘节点没有请求的内容,需要回源获取。 |
| **Hit Ratio** | 命中率 | 缓存命中次数占总请求次数的比例。命中率越高,回源越少,成本越低。 |
| **TTL** | 生存时间/缓存时间 | Time To Live,内容在 CDN 节点上缓存的有效期。过期后需要重新回源。 |
| **Back to Source** | 回源 | CDN 边缘节点向源站请求内容的过程。 |
| **Purge/Refresh** | 刷新缓存 | 强制使 CDN 缓存失效,下次请求回源获取最新内容。 |
| **Preheat** | 预热 | 在正式发布前,主动将内容推送到 CDN 节点,让用户第一次访问就能命中缓存。 |
| **CORS** | 跨域资源共享 | Cross-Origin Resource Sharing,浏览器的安全机制,控制不同域之间的资源访问。 |
| **Referer** | 来源页面 | HTTP 请求头字段,指示请求是从哪个页面链接过来的。用于防盗链。 |
| **STS** | 安全令牌服务 | Security Token Service,颁发临时访问凭证的服务,用于前端直传等场景。 |
| **Multipart Upload** | 分片上传 | 将大文件切分成多个小分片并行上传,支持断点续传,提高上传效率和可靠性。 |
| **ETag** | 实体标签 | HTTP 响应头,用于标识资源的特定版本,常用于缓存验证。 |
| **S3 API** | S3 兼容接口 | AWS S3 的对象存储 API 规范,多数云厂商的对象存储都兼容此接口。 |
| **Canonical Query String** | 规范查询字符串 | 签名字符串的一部分,用于计算请求签名,确保请求不被篡改。 |
---