fenghuo/packages/storage/docs/S3_DOWNLOAD_MECHANISM.md

2.8 KiB
Raw Blame History

S3存储下载机制说明

问题背景

在文件上传系统中,我们使用了两种存储类型:

  • 本地存储Local:文件存储在服务器本地文件系统
  • S3存储S3文件存储在AWS S3或兼容的对象存储服务中

对于文件访问,我们使用了目录格式的 fileId,例如:2025/05/28/RHwt8AkkZp

存储结构差异

本地存储

  • fileId2025/05/28/RHwt8AkkZp (目录路径)
  • 实际存储/uploads/2025/05/28/RHwt8AkkZp/filename.ext
  • 下载方式:扫描目录,找到实际文件,返回文件流

S3存储

  • fileId2025/05/28/RHwt8AkkZp (目录路径)
  • S3 Key2025/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. 下载流程

  1. 从数据库获取文件信息fileId + resource.title
  2. 重建完整S3 Key${fileId}/${fileName}
  3. 生成S3直接访问URL
  4. 302重定向到S3 URL让客户端直接从S3下载

优势

性能优势

  • 302重定向:避免服务器中转,减少带宽消耗
  • 直接下载客户端直接从S3下载速度更快
  • CDN友好可配合CloudFront等CDN使用

安全考虑

  • 公开读取需要确保S3 bucket配置了适当的公开读取权限
  • 预签名URL未来可扩展支持预签名URL用于私有文件

局限性

文件名依赖

  • 依赖数据库中存储的 resource.title 字段
  • 如果文件名不匹配会导致404错误

替代方案

如果需要更可靠的方案,可以考虑:

  1. 存储完整S3 Key在数据库中存储完整的S3对象路径
  2. 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