Files
chinese-family-tree-2/IMAGE_MIGRATION_DONE.md
T
freedakgmail 3d075c6076 0.0.8.5
2025-11-24 14:02:34 +08:00

307 lines
6.1 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.
# ✅ 图片存储迁移完成
## 📝 迁移概述
已成功将所有图片存储从 IndexedDB 迁移到服务器文件系统。
## 🔄 修改的文件
### 1. 核心组件
#### `components/ui/image-upload.tsx`
- ✅ 修改为上传到服务器 API
- ✅ value 改为 URL 而不是 ID
- ✅ 移除 IndexedDB 相关代码
- ✅ 添加错误处理
#### `components/ui/avatar-display.tsx`
- ✅ 简化为直接使用 URL
- ✅ 移除 IndexedDB 加载逻辑
- ✅ 移除 useEffect 和状态管理
#### `components/members/photo-gallery.tsx`
- ✅ 照片上传改为服务器 API
- ✅ photoIds 改为存储 URL 数组
- ✅ 移除 IndexedDB 相关代码
### 2. 页面组件
#### `app/members/[id]/page.tsx`
- ✅ 头像上传使用服务器 API
- ✅ 头像加载优先使用 avatarUrl
- ✅ 保留对旧 avatarImageId 的兼容
#### `app/members/page.tsx`
- ✅ 成员列表使用 avatarUrl
- ✅ 移除 avatarImageId 参数
#### `components/members/member-form.tsx`
- ✅ 表单使用 avatarUrl 字段
- ✅ 移除 avatarImageId 字段
### 3. 家族树组件
#### `components/tree/family-node.tsx`
- ✅ 使用 avatarUrl 而不是 avatarImageId
#### `components/tree/d3-org-chart-flow.tsx`
- ✅ 接口定义改为 avatarUrl
- ✅ 简化头像缓存逻辑
- ✅ 移除 IndexedDB 加载代码
#### `hooks/use-avatar-cache.ts`
- ✅ 简化为直接返回 URL
- ✅ 移除 IndexedDB 相关逻辑
## 📊 数据字段变更
### FamilyMember 类型
| 旧字段 | 新字段 | 说明 |
|--------|--------|------|
| `avatarImageId` | `avatarUrl` | 头像存储方式 |
| `photoIds` (ID数组) | `photoIds` (URL数组) | 照片墙存储方式 |
## 🎯 功能对比
### 之前(IndexedDB
```typescript
// 上传
const imageId = uuidv4()
await db.images.add({
id: imageId,
blob: file,
mimeType: file.type,
})
onChange(imageId)
// 显示
const image = await db.images.get(imageId)
const url = URL.createObjectURL(image.blob)
```
### 现在(服务器文件系统)
```typescript
// 上传
const formData = new FormData()
formData.append('file', file)
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
const { url } = await response.json()
onChange(url)
// 显示
<img src={url} />
```
## ✨ 优势
### 1. **跨设备访问**
- ✅ 所有用户都能看到图片
- ✅ 不同设备间同步
- ✅ 支持多用户协作
### 2. **数据持久化**
- ✅ 不会因清除浏览器数据而丢失
- ✅ 服务器端统一备份
- ✅ 更可靠的存储
### 3. **性能优化**
- ✅ 浏览器缓存
- ✅ 减少客户端存储压力
- ✅ 更快的加载速度
### 4. **易于管理**
- ✅ 集中式存储
- ✅ 便于备份和迁移
- ✅ 可以添加 CDN 加速
## 🔒 安全性
### 已实现
- ✅ 用户身份验证
- ✅ 文件类型验证
- ✅ 文件大小限制(5MB
- ✅ 唯一文件名生成
### 建议增强
- [ ] 图片水印
- [ ] 访问权限控制
- [ ] 定期清理未使用的图片
- [ ] 病毒扫描
## 📂 文件存储
### 目录结构
```
public/
└── uploads/
├── .gitkeep
├── 1732435200000-a3b5c7.jpg
├── 1732435201234-b4c6d8.jpg
└── ...
```
### Git 配置
```gitignore
# Uploads
public/uploads/*
!public/uploads/.gitkeep
```
## 🔄 兼容性
### 向后兼容
代码保留了对旧数据的兼容:
```typescript
// 优先使用新的 avatarUrl
if (member?.avatarUrl) {
setAvatarBlobUrl(member.avatarUrl)
}
// 兼容旧的 IndexedDB 存储
else if (member?.avatarImageId) {
db.images.get(member.avatarImageId).then((image) => {
if (image) {
const url = URL.createObjectURL(image.blob)
setAvatarBlobUrl(url)
}
})
}
```
### 数据迁移
如果有旧数据需要迁移,可以运行迁移脚本:
```typescript
// 迁移脚本示例
async function migrateImages() {
const members = await prisma.familyMember.findMany({
where: {
avatarImageId: { not: null }
}
})
for (const member of members) {
try {
// 从 IndexedDB 获取图片
const image = await db.images.get(member.avatarImageId!)
if (!image) continue
// 上传到服务器
const formData = new FormData()
formData.append('file', image.blob)
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
const { url } = await response.json()
// 更新数据库
await prisma.familyMember.update({
where: { id: member.id },
data: { avatarUrl: url }
})
console.log(`Migrated avatar for ${member.fullName}`)
} catch (error) {
console.error(`Failed to migrate ${member.fullName}:`, error)
}
}
}
```
## 🚀 部署注意事项
### 1. 确保目录存在
```bash
mkdir -p public/uploads
chmod 755 public/uploads
```
### 2. 环境变量
无需额外配置,直接使用本地文件系统
### 3. 备份策略
```bash
# 定期备份
rsync -av public/uploads/ /backup/uploads/
# 或使用 cron 定时任务
0 2 * * * rsync -av /path/to/app/public/uploads/ /backup/uploads/
```
### 4. 磁盘监控
```bash
# 查看上传目录大小
du -sh public/uploads
# 监控磁盘使用
df -h
```
## 📈 性能优化建议
### 1. 图片压缩
已实现:
- 头像:最大 1MB1024x1024
- 照片:最大 2MB1920x1920
### 2. 浏览器缓存
Next.js 自动处理 `public` 目录的缓存
### 3. CDN 加速(可选)
```nginx
location /uploads/ {
alias /path/to/app/public/uploads/;
expires 30d;
add_header Cache-Control "public, immutable";
}
```
### 4. 懒加载
```tsx
<img
src={avatarUrl}
loading="lazy"
alt="头像"
/>
```
## ✅ 测试清单
- [x] 头像上传功能
- [x] 头像显示功能
- [x] 照片墙上传
- [x] 照片墙显示
- [x] 成员列表头像
- [x] 成员详情头像
- [x] 家族树节点头像
- [x] D3 组织图头像
- [x] 成员表单头像上传
- [x] 向后兼容性
## 🎉 迁移完成
所有图片存储已成功从 IndexedDB 迁移到服务器文件系统!
### 主要改进
1. ✅ 跨设备访问
2. ✅ 数据持久化
3. ✅ 多用户协作
4. ✅ 更好的性能
5. ✅ 易于备份和管理
### 下一步
- 测试所有功能
- 迁移现有数据(如果有)
- 监控磁盘使用
- 考虑添加 CDN(可选)