111 lines
2.8 KiB
Markdown
111 lines
2.8 KiB
Markdown
![]() |
# 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路径:
|
|||
|
|
|||
|
```typescript
|
|||
|
const fileName = resource.title || 'file';
|
|||
|
const fullS3Key = `${fileId}/${fileName}`;
|
|||
|
```
|
|||
|
|
|||
|
### 2. URL生成逻辑
|
|||
|
|
|||
|
```typescript
|
|||
|
// 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配置正确:
|
|||
|
|
|||
|
```env
|
|||
|
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
|
|||
|
```
|