AWS S3
欢迎来到 AWS S3 知识库!
☁️ S3 简介
Amazon S3 (Simple Storage Service) 是 AWS 提供的对象存储服务,是云存储的事实标准。自 2006 年推出以来,S3 已成为对象存储领域最成熟、功能最全面的服务。
🎯 S3 核心特性
✅ 主要优势
- 99.999999999% 持久性 - 11 个 9 的数据持久性
- 99.99% 可用性 - 高可用性保证
- 无限扩展 - 存储容量无限制
- 全球部署 - 多个区域可选
- 丰富功能 - 版本控制、生命周期、事件通知等
- 生态完善 - 与 AWS 其他服务无缝集成
📦 S3 核心概念
1. Bucket(存储桶)
特点:
- 全球唯一的名称(如 my-app-images)
- 属于特定区域(如 us-east-1)
- 根级别容器,不能嵌套
- 每个账户最多 100 个 bucket(可申请提额)
命名规则:
- 3-63 个字符
- 只能包含小写字母、数字、连字符和点号
- 不能以连字符或点号开头/结尾
- 不能使用 IP 地址格式2. Object(对象)
对象组成:
├── Key: 对象的唯一标识符
│ 例: images/2024/product-123.jpg
├── Value: 对象数据(0 字节到 5TB)
├── Metadata: 元数据
│ ├── 系统元数据
│ │ - Content-Type
│ │ - Content-Length
│ │ - Last-Modified
│ │ - ETag
│ └── 用户定义元数据
│ - x-amz-meta-*
├── Version ID: 版本标识(开启版本控制后)
├── Access Control: 访问控制
└── Storage Class: 存储类别3. Key(对象键)
S3 是扁平化存储,没有真正的目录:
key = "images/products/2024/01/photo.jpg"
看起来像目录结构,实际上:
- 整个字符串是一个 key
- 使用 / 分隔只是命名约定
- S3 控制台会模拟目录显示
最佳实践:
- 使用有意义的前缀组织对象
- 考虑性能优化(避免连续前缀)
- 避免特殊字符🚀 快速开始
1. 安装 AWS SDK
bash
# Node.js
npm install aws-sdk
# Python
pip install boto3
# Java
# 添加到 pom.xml
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.12.x</version>
</dependency>
# Go
go get github.com/aws/aws-sdk-go/service/s32. 配置凭证
bash
# 方式一: 环境变量
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_DEFAULT_REGION=us-east-1
# 方式二: AWS CLI 配置
aws configure
# 方式三: 配置文件 (~/.aws/credentials)
[default]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key3. Node.js 示例
javascript
const AWS = require('aws-sdk');
// 配置 S3
const s3 = new AWS.S3({
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});
// 创建 Bucket
const createBucket = async (bucketName) => {
try {
await s3.createBucket({ Bucket: bucketName }).promise();
console.log(`Bucket ${bucketName} created`);
} catch (err) {
console.error(err);
}
};
// 上传文件
const uploadFile = async (bucketName, key, fileContent) => {
const params = {
Bucket: bucketName,
Key: key,
Body: fileContent,
ContentType: 'image/jpeg',
// ACL: 'public-read', // 公开读取
Metadata: {
'author': 'John Doe',
'uploaded-by': 'my-app'
}
};
try {
const result = await s3.upload(params).promise();
console.log('File uploaded:', result.Location);
return result;
} catch (err) {
console.error(err);
}
};
// 下载文件
const getFile = async (bucketName, key) => {
const params = {
Bucket: bucketName,
Key: key
};
try {
const data = await s3.getObject(params).promise();
return data.Body;
} catch (err) {
console.error(err);
}
};
// 列出对象
const listObjects = async (bucketName, prefix) => {
const params = {
Bucket: bucketName,
Prefix: prefix,
MaxKeys: 100
};
try {
const data = await s3.listObjectsV2(params).promise();
return data.Contents;
} catch (err) {
console.error(err);
}
};
// 删除对象
const deleteFile = async (bucketName, key) => {
const params = {
Bucket: bucketName,
Key: key
};
try {
await s3.deleteObject(params).promise();
console.log(`File ${key} deleted`);
} catch (err) {
console.error(err);
}
};
// 批量删除
const deleteMultipleFiles = async (bucketName, keys) => {
const params = {
Bucket: bucketName,
Delete: {
Objects: keys.map(key => ({ Key: key }))
}
};
try {
const result = await s3.deleteObjects(params).promise();
console.log('Deleted:', result.Deleted.length);
return result;
} catch (err) {
console.error(err);
}
};4. Python (Boto3) 示例
python
import boto3
from botocore.exceptions import ClientError
# 创建 S3 客户端
s3 = boto3.client('s3',
region_name='us-east-1',
aws_access_key_id='your_access_key',
aws_secret_access_key='your_secret_key'
)
# 上传文件
def upload_file(bucket_name, file_path, object_key):
try:
s3.upload_file(file_path, bucket_name, object_key)
print(f"File uploaded to {object_key}")
except ClientError as e:
print(f"Error: {e}")
# 下载文件
def download_file(bucket_name, object_key, file_path):
try:
s3.download_file(bucket_name, object_key, file_path)
print(f"File downloaded to {file_path}")
except ClientError as e:
print(f"Error: {e}")
# 生成预签名 URL
def generate_presigned_url(bucket_name, object_key, expiration=3600):
try:
url = s3.generate_presigned_url(
'get_object',
Params={'Bucket': bucket_name, 'Key': object_key},
ExpiresIn=expiration
)
return url
except ClientError as e:
print(f"Error: {e}")
return None🔐 预签名 URL
预签名 URL 允许临时授权访问私有对象,无需公开对象权限。
Node.js 实现
javascript
// GET 预签名 URL (下载)
const getPresignedUrl = (bucketName, key, expiresIn = 3600) => {
const params = {
Bucket: bucketName,
Key: key,
Expires: expiresIn // 秒
};
return s3.getSignedUrl('getObject', params);
};
// PUT 预签名 URL (上传)
const getUploadPresignedUrl = (bucketName, key, contentType, expiresIn = 3600) => {
const params = {
Bucket: bucketName,
Key: key,
ContentType: contentType,
Expires: expiresIn
};
return s3.getSignedUrl('putObject', params);
};
// 使用示例
const url = getPresignedUrl('my-bucket', 'images/photo.jpg', 3600);
console.log('Download URL:', url);
// 前端可以直接使用这个 URL 下载文件
// <a href="${url}" download>Download</a>前端直传实现
javascript
// 后端生成上传 URL
app.post('/api/get-upload-url', async (req, res) => {
const { fileName, fileType } = req.body;
const key = `uploads/${Date.now()}-${fileName}`;
const url = s3.getSignedUrl('putObject', {
Bucket: 'my-bucket',
Key: key,
ContentType: fileType,
Expires: 300 // 5分钟
});
res.json({ url, key });
});
// 前端使用 URL 直传
async function uploadFile(file) {
// 1. 获取预签名 URL
const response = await fetch('/api/get-upload-url', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fileName: file.name,
fileType: file.type
})
});
const { url, key } = await response.json();
// 2. 直接上传到 S3
await fetch(url, {
method: 'PUT',
body: file,
headers: {
'Content-Type': file.type
}
});
console.log('File uploaded:', key);
}📊 存储类别
存储类别对比
| 存储类别 | 使用场景 | 可用性 | 检索时间 | 最短存储时间 | 相对成本 |
|---|---|---|---|---|---|
| S3 Standard | 频繁访问 | 99.99% | 毫秒 | 无 | 1.0x |
| S3 Intelligent-Tiering | 访问模式未知 | 99.9% | 毫秒 | 30天 | 0.8-1.0x |
| S3 Standard-IA | 低频访问 | 99.9% | 毫秒 | 30天 | 0.5x |
| S3 One Zone-IA | 低频访问(单区) | 99.5% | 毫秒 | 30天 | 0.4x |
| S3 Glacier Instant | 归档即时访问 | 99.9% | 毫秒 | 90天 | 0.2x |
| S3 Glacier Flexible | 归档 | 99.99% | 分钟-小时 | 90天 | 0.1x |
| S3 Glacier Deep Archive | 长期归档 | 99.99% | 12小时 | 180天 | 0.04x |
设置存储类别
javascript
// 上传时指定
const uploadWithStorageClass = async (bucketName, key, body) => {
const params = {
Bucket: bucketName,
Key: key,
Body: body,
StorageClass: 'STANDARD_IA' // 低频访问
};
await s3.upload(params).promise();
};
// 修改现有对象的存储类别
const changeStorageClass = async (bucketName, key) => {
const params = {
Bucket: bucketName,
CopySource: `${bucketName}/${key}`,
Key: key,
StorageClass: 'GLACIER',
MetadataDirective: 'COPY'
};
await s3.copyObject(params).promise();
};🔄 生命周期管理
自动转换存储类别或删除对象。
javascript
const setLifecyclePolicy = async (bucketName) => {
const params = {
Bucket: bucketName,
LifecycleConfiguration: {
Rules: [
{
Id: 'Move to IA after 30 days',
Status: 'Enabled',
Prefix: 'logs/', // 仅应用于 logs/ 前缀
Transitions: [
{
Days: 30,
StorageClass: 'STANDARD_IA'
},
{
Days: 90,
StorageClass: 'GLACIER'
}
],
Expiration: {
Days: 365 // 1年后删除
}
},
{
Id: 'Delete incomplete multipart uploads',
Status: 'Enabled',
AbortIncompleteMultipartUpload: {
DaysAfterInitiation: 7
}
}
]
}
};
await s3.putBucketLifecycleConfiguration(params).promise();
};📝 版本控制
保留对象的多个版本,防止意外删除或覆盖。
javascript
// 启用版本控制
const enableVersioning = async (bucketName) => {
const params = {
Bucket: bucketName,
VersioningConfiguration: {
Status: 'Enabled'
}
};
await s3.putBucketVersioning(params).promise();
};
// 列出对象的所有版本
const listVersions = async (bucketName, key) => {
const params = {
Bucket: bucketName,
Prefix: key
};
const data = await s3.listObjectVersions(params).promise();
return data.Versions;
};
// 下载特定版本
const getObjectVersion = async (bucketName, key, versionId) => {
const params = {
Bucket: bucketName,
Key: key,
VersionId: versionId
};
const data = await s3.getObject(params).promise();
return data.Body;
};
// 删除特定版本
const deleteObjectVersion = async (bucketName, key, versionId) => {
const params = {
Bucket: bucketName,
Key: key,
VersionId: versionId
};
await s3.deleteObject(params).promise();
};🔔 事件通知
S3 可以在对象创建、删除时触发事件。
javascript
// 配置事件通知到 Lambda
const setEventNotification = async (bucketName, lambdaArn) => {
const params = {
Bucket: bucketName,
NotificationConfiguration: {
LambdaFunctionConfigurations: [
{
Events: ['s3:ObjectCreated:*'],
LambdaFunctionArn: lambdaArn,
Filter: {
Key: {
FilterRules: [
{
Name: 'prefix',
Value: 'uploads/'
},
{
Name: 'suffix',
Value: '.jpg'
}
]
}
}
}
]
}
};
await s3.putBucketNotificationConfiguration(params).promise();
};💡 最佳实践
1. 命名规范
javascript
// ✅ 好的命名
const key = `users/${userId}/avatars/${timestamp}-${uuid}.jpg`;
const key = `invoices/${year}/${month}/${invoiceId}.pdf`;
// ❌ 避免
const key = `用户头像.jpg`; // 避免中文
const key = `file(1).jpg`; // 避免特殊字符2. 性能优化
javascript
// 避免连续的字母数字前缀(会导致分区热点)
// ❌ 不好
user-001/...
user-002/...
user-003/...
// ✅ 好 - 添加随机前缀
const hash = md5(userId).substring(0, 4);
const key = `${hash}/users/${userId}/avatar.jpg`;3. 成本优化
javascript
// 使用生命周期策略自动归档
// 定期清理未完成的分片上传
// 使用智能分层存储📖 学习资源
官方文档
推荐阅读
准备好了吗?开始使用 AWS S3!