2.8 KiB
2.8 KiB
S3存储下载机制说明
问题背景
在文件上传系统中,我们使用了两种存储类型:
- 本地存储(Local):文件存储在服务器本地文件系统
- S3存储(S3):文件存储在AWS S3或兼容的对象存储服务中
对于文件访问,我们使用了目录格式的 fileId
,例如:2025/05/28/RHwt8AkkZp
存储结构差异
本地存储
- fileId:
2025/05/28/RHwt8AkkZp
(目录路径) - 实际存储:
/uploads/2025/05/28/RHwt8AkkZp/filename.ext
- 下载方式:扫描目录,找到实际文件,返回文件流
S3存储
- fileId:
2025/05/28/RHwt8AkkZp
(目录路径) - S3 Key:
2025/05/28/RHwt8AkkZp/filename.ext
(完整对象路径) - 下载方式:重定向到S3 URL
核心问题
S3存储中,对象的完整路径(S3 Key)包含文件名,但我们的 fileId
只是目录路径,缺少文件名部分。
解决方案
1. 文件名重建策略
我们通过以下方式重建完整的S3路径:
const fileName = resource.title || 'file';
const fullS3Key = `${fileId}/${fileName}`;
2. URL生成逻辑
// AWS S3
const s3Url = `https://${bucket}.s3.${region}.amazonaws.com/${fullS3Key}`;
// 自定义S3兼容服务(如MinIO)
const s3Url = `${endpoint}/${bucket}/${fullS3Key}`;
3. 下载流程
- 从数据库获取文件信息(fileId + resource.title)
- 重建完整S3 Key:
${fileId}/${fileName}
- 生成S3直接访问URL
- 302重定向到S3 URL,让客户端直接从S3下载
优势
性能优势
- 302重定向:避免服务器中转,减少带宽消耗
- 直接下载:客户端直接从S3下载,速度更快
- CDN友好:可配合CloudFront等CDN使用
安全考虑
- 公开读取:需要确保S3 bucket配置了适当的公开读取权限
- 预签名URL:未来可扩展支持预签名URL用于私有文件
局限性
文件名依赖
- 依赖数据库中存储的
resource.title
字段 - 如果文件名不匹配,会导致404错误
替代方案
如果需要更可靠的方案,可以考虑:
- 存储完整S3 Key:在数据库中存储完整的S3对象路径
- S3 ListObjects API:动态查询S3中的实际对象(会增加API调用成本)
环境配置
确保S3配置正确:
STORAGE_TYPE=s3
S3_BUCKET=your-bucket-name
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
S3_ENDPOINT=https://s3.amazonaws.com # 可选,用于其他S3兼容服务
测试验证
使用以下URL格式测试下载:
/download/2025%2F05%2F28%2FRHwt8AkkZp
应该会302重定向到:
https://your-bucket.s3.us-east-1.amazonaws.com/2025/05/28/RHwt8AkkZp/filename.ext