# 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
```
