大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
S3 存储
这一课你将能做到
- 创建 S3 桶、上传下载文件,并配置生命周期规则自动归档到 Glacier
- 说出 S3 各存储类别的区别(Standard、IA、Intelligent-Tiering、Glacier),并给出选型建议
- 给本站配置一个用户头像/文件上传方案,用预签名 URL 实现前端直传 S3
为什么要学
S3 是 AWS 的「基石服务」,几乎是一切云上存储的起点。本站如果迁到 AWS,用户头像、课程资料、代码附件都需要一个高可用、低成本的存储方案。S3 的 99.999999999%(11 个 9)持久性让它成为对象存储的行业标杆。面试中「S3 存储类别怎么选」「怎么实现前端直传」「生命周期规则怎么配」是高频问题。
人话:S3 就是一个无限容量的网盘,但你不能把它当硬盘用。它是「对象存储」——你扔进去一个文件,它给你一个 key(文件名),你通过 HTTP API 存取。不能像本地磁盘一样改文件的一部分,只能整个覆盖。
先建立图像
S3 存储类别对比:
| 存储类别 | 检索时间 | 持久性 | 适用场景 | 本站建议 |
|---|---|---|---|---|
| Standard | 毫秒级 | 11个9 | 频繁访问的热数据 | 用户头像、最新附件 |
| Standard-IA | 毫秒级 | 11个9 | 不常访问但需快速取回 | 30天前的课程资料 |
| One Zone-IA | 毫秒级 | 11个9(单AZ) | 可重建的数据 | 中间缓存文件 |
| Intelligent-Tiering | 毫秒级 | 11个9 | 访问模式未知 | 本站首选(自动分层) |
| Glacier Instant | 毫秒级 | 11个9 | 长期归档但需即时取回 | 备份 |
| Glacier Flexible | 分钟-小时级 | 11个9 | 长期备份 | 日志归档 |
| Glacier Deep Archive | 小时级 | 11个9 | 合规存档(7-10年) | 审计日志 |
S3 架构图:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户/前端 │────→│ 预签名URL │────→│ S3 桶 │
│ │ │(临时授权) │ │(codenav-*) │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
┌───────┴───────┐
│ 生命周期规则 │
│ 30天→IA │
│ 90天→Glacier │
│ 365天→Deep │
└───────────────┘
跟着做
步骤 1:创建 S3 桶
# 创建桶(区域唯一名)
aws s3 mb s3://codenav-uploads-2024 --region ap-southeast-1
# 开启版本控制(防误删)
aws s3api put-bucket-versioning --bucket codenav-uploads-2024 --versioning-configuration Status=Enabled
# 设置默认加密(SSE-S3)
aws s3api put-bucket-encryption --bucket codenav-uploads-2024 --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
步骤 2:上传和下载文件
# 上传文件
aws s3 cp ./avatar.jpg s3://codenav-uploads-2024/avatars/user1.jpg
# 下载文件
aws s3 cp s3://codenav-uploads-2024/avatars/user1.jpg ./downloaded.jpg
# 列出桶内容
aws s3 ls s3://codenav-uploads-2024/avatars/
# 同步整个目录
aws s3 sync ./static s3://codenav-uploads-2024/static/
步骤 3:配置生命周期规则
创建 lifecycle.json:
{
"Rules": [
{
"ID": "MoveToIA",
"Status": "Enabled",
"Filter": {"Prefix": "attachments/"},
"Transitions": [
{"Days": 30, "StorageClass": "STANDARD_IA"},
{"Days": 90, "StorageClass": "GLACIER"},
{"Days": 365, "StorageClass": "DEEP_ARCHIVE"}
]
},
{
"ID": "DeleteOldVersions",
"Status": "Enabled",
"Filter": {"Prefix": ""},
"NoncurrentVersionTransitions": [
{"NoncurrentDays": 30, "StorageClass": "GLACIER"}
],
"NoncurrentVersionExpiration": {"NoncurrentDays": 90}
}
]
}
aws s3api put-bucket-lifecycle-configuration --bucket codenav-uploads-2024 --lifecycle-configuration file://lifecycle.json
步骤 4:配置 CORS 允许前端直传
aws s3api put-bucket-cors --bucket codenav-uploads-2024 --cors-configuration '{
"CORSRules": [
{
"AllowedOrigins": ["https://codenav.example.com"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3000
}
]
}'
步骤 5:生成预签名 URL(前端直传)
后端 Java 代码(Spring Boot):
// 注入 AmazonS3 client
@Autowired
private AmazonS3 s3Client;
public String generatePresignedUrl(String fileName, String contentType) {
Date expiration = new Date(System.currentTimeMillis() + 3600_000); // 1小时有效
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest("codenav-uploads-2024", "avatars/" + fileName)
.withMethod(HttpMethod.PUT)
.withExpiration(expiration);
request.addRequestParameter("Content-Type", contentType);
URL url = s3Client.generatePresignedUrl(request);
return url.toString();
}
前端 JavaScript:
// 1. 从后端获取预签名 URL
const response = await fetch('/api/upload/presigned?fileName=avatar.jpg&contentType=image/jpeg');
const { url } = await response.json();
// 2. 直接上传到 S3(不经过后端服务器)
await fetch(url, {
method: 'PUT',
body: file,
headers: { 'Content-Type': 'image/jpeg' }
});
步骤 6:用 CloudFront 加速访问
# 创建 CloudFront 分发,源站指向 S3
aws cloudfront create-distribution --origin-domain-name codenav-uploads-2024.s3.ap-southeast-1.amazonaws.com --default-root-object index.html
讲清楚要点
- 桶名全局唯一:S3 的命名空间是全球的,不是按账号隔离。命名要加随机后缀或公司域名。
- 版本控制:开启后删除/覆盖不会真删,会生成旧版本。配合 MFA Delete 可以防止恶意删除。但版本控制会增加存储成本,要配生命周期规则清理旧版本。
- 预签名 URL:让客户端直接上传/下载 S3,不经过后端服务器,减轻后端压力。URL 有过期时间,过期后失效。
- S3 一致性模型:2020 年后 S3 是强一致性的,PUT 后立即可读,覆盖写后立即可读最新版本。
- 事件通知:S3 可以配置事件(ObjectCreated、ObjectRemoved),触发 Lambda、SQS、SNS。比如用户上传头像后自动用 Lambda 压缩。
你可能会懵的地方
-
S3 和 EBS 有什么区别? S3 是对象存储,通过 HTTP API 访问,不能挂载为文件系统。EBS 是块存储,挂载到 EC2 当硬盘用,可以格式化成 ext4/xfs。S3 便宜持久,EBS 高性能但贵。
-
S3 能当数据库用吗? 不能。S3 没有查询能力,只能按 key 取整个对象。要查询得配合 Athena(Serverless SQL)或把元数据存 DynamoDB。
-
S3 的文件夹是什么? S3 没有真正的文件夹,key 里的
/只是前缀。avatars/user1.jpg的 key 就是完整字符串,AWS 控制台为了展示才显示成文件夹。 -
怎么限制 S3 桶只能被特定 VPC 访问? 用 VPC Endpoint(Gateway Endpoint for S3),让流量走 AWS 内网,不经过互联网。再配合桶策略限制
aws:SourceVpce。 -
S3 静态网站托管和 CloudFront 有什么区别? S3 静态网站托管是 HTTP 服务,不支持 HTTPS(自定义域名)。CloudFront 是 CDN,支持 HTTPS、自定义域名、缓存、边缘计算。生产环境用 CloudFront + S3。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 把 S3 桶设为公开读写 | 任何人都能上传/删除,被当免费网盘 | 默认私有,用预签名 URL 或 CloudFront OAC |
| 不用版本控制 | 误删/覆盖后无法恢复 | 开启版本控制,配生命周期清理旧版本 |
| 所有文件都放 Standard | 冷数据浪费钱 | 配生命周期规则,自动降级到 IA/Glacier |
| 前端直传不设文件类型限制 | 用户上传恶意文件 | 预签名 URL 里限制 Content-Type,后端校验扩展名 |
| 把大文件放 Glacier 但经常取 | 检索费比存储费贵 | Glacier 只放真正不常访问的数据 |
卡点急救
- 上传大文件失败:单文件超过 5GB 必须用分段上传(Multipart Upload)。AWS CLI 的
aws s3 cp会自动处理,SDK 需要显式启用。 - CloudFront 回源 403:S3 桶策略没允许 CloudFront 的 Origin Access Control(OAC)。更新桶策略,允许
cloudfront.amazonaws.com服务主体访问。 - 预签名 URL 报 SignatureDoesNotMatch:Content-Type 不匹配,或 URL 过期,或系统时间不对(AWS 要求时间差在 15 分钟内)。
面试常问
-
S3 的存储类别怎么选? 看访问频率和检索时间要求。热数据用 Standard,温数据用 Standard-IA 或 Intelligent-Tiering,冷数据用 Glacier。Intelligent-Tiering 适合访问模式不确定的场景,自动分层,每月每对象收少量监控费。
-
S3 怎么实现高可用? S3 本身就是多 AZ 冗余的(11 个 9 持久性)。跨区域容灾用 Cross-Region Replication(CRR),自动同步到另一个 Region 的桶。
-
S3 事件通知有哪些用途? 图片上传后自动压缩(Lambda),日志文件到达后触发 ETL(Glue/EMR),文件删除后同步清理数据库记录(SQS + 消费者)。
自测三问
- S3 Standard 和 Glacier Deep Archive 的检索时间分别是多少?成本差多少倍?
- 预签名 URL 的原理是什么?为什么能减轻后端压力?
- 开启版本控制后,删除一个文件会发生什么?存储成本怎么变?
小结一句话
S3 是云上对象存储的标杆,桶名全球唯一、默认私有、版本控制防误删、生命周期自动省钱、预签名 URL 实现直传。
延伸
- AWS S3 官方文档
- S3 存储类别详解
- 下一课:RDS 数据库——学会给本站搭一个高可用的 MySQL。