Files
raisfast/docs/media-manager.md
T
2026-04-19 14:45:37 +08:00

296 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 资源管理器设计文档
## 概述
通用的文件资源管理器,支持上传、在线预览、分类管理、搜索筛选。作为后台管理核心模块,同时服务于编辑器(插入图片/视频/附件)和独立管理场景。
## 功能清单
### 上传
| 功能 | 说明 |
|---|---|
| 拖拽上传 | 拖文件到页面直接上传,支持多文件批量 |
| 粘贴上传 | Ctrl+V 粘贴剪贴板图片(截图直接上传) |
| 按钮上传 | 点击按钮选择文件,支持多选 |
| 上传进度 | 每个文件显示独立进度条,失败可重试 |
| 格式校验 | 前后端双重校验 MIME 类型 + magic bytes |
### 文件分类
| 分类 | MIME 类型 | 图标 |
|---|---|---|
| 图片 | image/jpeg, image/png, image/gif, image/webp, image/svg+xml | `Image` |
| 视频 | video/mp4, video/webm, video/quicktime | `Video` |
| 音频 | audio/mpeg, audio/ogg, audio/wav, audio/aac | `Music` |
| 文档 | application/pdf, application/msword, application/vnd.* | `FileText` |
| 表格 | application/vnd.ms-excel, application/vnd.openxmlformats-* | `Sheet` |
| 压缩包 | application/zip, application/x-tar, application/gzip | `Archive` |
| 其他 | 以上未覆盖的类型 | `File` |
### 浏览
| 功能 | 说明 |
|---|---|
| 网格视图 | 缩略图卡片,显示文件名、大小、类型图标 |
| 列表视图 | 表格形式,显示文件名、类型、大小、上传时间、操作按钮 |
| 分类筛选 | 左侧侧边栏按文件类型分类,点击切换 |
| 搜索 | 按文件名模糊搜索 |
| 排序 | 按上传时间(默认)、文件名、文件大小排序 |
| 分页 | 每页 20/40/60 条,支持翻页 |
### 预览
| 文件类型 | 预览方式 |
|---|---|
| 图片 | 灯箱放大,支持缩放、左右切换 |
| 视频 | 内嵌播放器(`<video>`),播放/暂停/进度条 |
| 音频 | 内嵌播放器(`<audio>`),播放/暂停/进度条 |
| PDF | `<iframe>` 或 `<embed>` 内嵌预览 |
| Word/Excel | 提示下载(浏览器无法直接预览) |
| 其他 | 显示文件信息,提供下载链接 |
### 文件操作
| 操作 | 说明 |
|---|---|
| 重命名 | 弹出输入框修改文件名 |
| 复制 URL | 一键复制文件公开访问地址到剪贴板 |
| 下载 | 直接下载文件 |
| 删除 | 确认后删除(仅所有者或管理员) |
| 批量选择 | 网格视图支持多选,批量删除 |
### 编辑器集成
| 触发方式 | 行为 |
|---|---|
| 编辑器点击"插入图片" | 弹出资源管理器 → 选图 → 插入 `![](url)` |
| 编辑器点击"插入视频" | 弹出资源管理器 → 选视频 → 插入 `<video>` |
| 编辑器点击"插入链接" | 弹出资源管理器 → 选文件 → 插入 `[text](url)` |
| 编辑器粘贴/拖拽图片 | 直接上传并插入 |
## 页面布局
```
┌─────────────────────────────────────────────────────────┐
│ 资源管理器 已用 128MB / 1GB [网格] [列表] │
├──────────┬──────────────────────────────────────────────┤
│ │ [上传文件 ▼] 🔍 搜索... 排序: 最新 ↑ │
│ 全部 (42) │──────────────────────────────────────────────│
│ │ │
│ 🖼 图片(15)│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ 🎬 视频(3) │ │ 🖼 │ │ 🖼 │ │ 🎬 │ │ 📄 │ │
│ 🎵 音频(5) │ │cover │ │photo │ │demo │ │report │ │
│ 📄 文档(8) │ │.jpg │ │.png │ │.mp4 │ │.pdf │ │
│ 📊 表格(4) │ │ 2.1MB │ │ 450KB │ │ 25MB │ │ 1.2MB │ │
│ 📦 压缩(2) │ └──────┘ └──────┘ └──────┘ └──────┘ │
│ 📎 其他(5) │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ │ 📊 │ │ 🎵 │ │ 📦 │ │ │ │
│ │ │sales │ │bgm │ │backup │ │ │ │
│ │ │.xlsx │ │.mp3 │ │.zip │ │ │ │
│ │ │ 3.5MB │ │ 4.1MB │ │ 12MB │ │ │ │
│ │ └──────┘ └──────┘ └──────┘ └──────┘ │
│ │ │
│ │ ← 1 2 3 → 每页 20 条 │
└──────────┴──────────────────────────────────────────────┘
```
### 详情面板(选中文件时右侧展开)
```
┌────────────────────────┐
│ 🖼 cover.jpg │
│ ┌──────────────────┐ │
│ │ │ │
│ │ [图片预览] │ │
│ │ │ │
│ └──────────────────┘ │
│ │
│ 文件名 cover.jpg │
│ 类型 image/jpeg │
│ 大小 2.1 MB │
│ 上传者 admin │
│ 上传时间 2026-04-19 │
│ │
│ URL │
│ ┌──────────────────┐ │
│ │ http://.../cover │📋│
│ └──────────────────┘ │
│ │
│ [重命名] [下载] [删除] │
└────────────────────────┘
```
## API 设计
### 现有接口调整
| 接口 | 变更 |
|---|---|
| `POST /api/v1/media/upload` | 放宽 MIME 白名单,支持所有类型 |
### 新增接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/api/v1/media/stats` | 存储统计(已用空间、文件数量按类型分组) |
#### GET /api/v1/media/stats 响应
```json
{
"code": 0,
"data": {
"total_size": 134217728,
"total_files": 42,
"by_type": {
"image": { "count": 15, "size": 31457280 },
"video": { "count": 3, "size": 78643200 },
"audio": { "count": 5, "size": 20971520 },
"document": { "count": 8, "size": 2097152 },
"spreadsheet": { "count": 4, "size": 1048576 },
"archive": { "count": 2, "size": 12582912 },
"other": { "count": 5, "size": 1048576 }
}
}
}
```
## 前端文件结构
```
web/src/app/admin/media/
page.tsx ← 资源管理器主页面
web/src/components/admin/media/
media-grid.tsx ← 网格视图
media-list.tsx ← 列表视图
media-upload.tsx ← 上传区域(拖拽 + 粘贴 + 按钮)
media-upload-item.tsx ← 单个上传进度条
media-preview.tsx ← 预览弹窗(图片/视频/音频/PDF)
media-sidebar.tsx ← 左侧分类筛选栏
media-detail-panel.tsx ← 右侧文件详情面板
media-actions.tsx ← 文件操作菜单(重命名/复制/下载/删除)
media-selector.tsx ← 编辑器内嵌选择器(选择并插入)
```
### 组件关系
```
page.tsx
├── media-sidebar.tsx 分类筛选
├── media-upload.tsx 上传区域
│ └── media-upload-item.tsx 进度条 × N
├── media-grid.tsx 网格视图
│ └── media-actions.tsx 每个文件的操作按钮
├── media-list.tsx 列表视图
│ └── media-actions.tsx
├── media-detail-panel.tsx 详情 + 预览
│ └── media-preview.tsx 预览组件
└── media-selector.tsx 编辑器调用时使用
└── media-grid.tsx
```
## 后端改动
### 1. 放宽 MIME 白名单
```rust
// src/services/media.rs
const ALLOWED_TYPES: &[&str] = &[
// 图片
"image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml",
// 视频
"video/mp4", "video/webm", "video/quicktime",
// 音频
"audio/mpeg", "audio/ogg", "audio/wav", "audio/aac",
// 文档
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
// 压缩
"application/zip", "application/x-tar", "application/gzip", "application/x-rar-compressed",
// 文本
"text/plain", "text/csv", "text/markdown",
];
```
### 2. 扩展 magic bytes
增加 ZIP、PDF 等通用格式的签名校验,对无法校验的类型(纯文本等)跳过 magic bytes 检查。
### 3. 新增 stats handler
```rust
// src/handlers/media.rs
pub async fn stats(
State(state): State<crate::AppState>,
auth_user: AuthUser,
tenant: ResolvedTenant,
) -> AppResult<ApiResponse<MediaStatsResponse>>
```
### 4. 缩略图生成(可选 Phase 2)
利用已有的 `image` crate,上传图片时自动生成缩略图(200x200),存储为 `{key}.thumb.jpg`,用于网格视图快速加载。
## 上传大小限制
| 场景 | 建议值 |
|---|---|
| 图片 | 10MB |
| 视频 | 100MB |
| 其他 | 50MB |
| 总上限 | `MAX_UPLOAD_SIZE` 环境变量控制,默认 100MB |
前端按类型做预校验,超出直接提示。
## 编辑器集成方式
### 调用流程
```
用户点击编辑器"插入图片"按钮
↓
打开 Dialog 内嵌 media-selector.tsx
↓
media-selector 只显示图片类型,隐藏侧边栏
↓
用户选择一张图片(或上传新图)
↓
点击"插入"按钮
↓
返回 { url, alt } 给编辑器
↓
编辑器插入 ![alt](url)
```
### media-selector.tsx Props
```typescript
interface MediaSelectorProps {
/** 过滤显示的文件类型 */
filterType?: "image" | "video" | "audio" | "document" | "all";
/** 是否允许多选 */
multiple?: boolean;
/** 选择确认回调 */
onSelect: (files: MediaFile[]) => void;
/** 取消回调 */
onCancel: () => void;
}
```
## 实施计划
| 阶段 | 内容 | 预估 |
|---|---|---|
| **P1** | 放宽后端 MIME + 前端网格/列表视图 + 上传 + 搜索排序 | 1-2 天 |
| **P2** | 在线预览(图片灯箱 + 视频/音频播放 + PDF) | 0.5 天 |
| **P3** | 详情面板 + 文件操作(重命名/复制 URL/删除) | 0.5 天 |
| **P4** | 编辑器集成(media-selector) | 0.5 天 |
| **P5** | 存储统计 + 缩略图生成 | 1 天 |