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

6.1 KiB
Raw Blame History

图片存储迁移完成

📝 迁移概述

已成功将所有图片存储从 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

// 上传
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)

现在(服务器文件系统)

// 上传
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 配置

# Uploads
public/uploads/*
!public/uploads/.gitkeep

🔄 兼容性

向后兼容

代码保留了对旧数据的兼容:

// 优先使用新的 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)
    }
  })
}

数据迁移

如果有旧数据需要迁移,可以运行迁移脚本:

// 迁移脚本示例
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. 确保目录存在

mkdir -p public/uploads
chmod 755 public/uploads

2. 环境变量

无需额外配置,直接使用本地文件系统

3. 备份策略

# 定期备份
rsync -av public/uploads/ /backup/uploads/

# 或使用 cron 定时任务
0 2 * * * rsync -av /path/to/app/public/uploads/ /backup/uploads/

4. 磁盘监控

# 查看上传目录大小
du -sh public/uploads

# 监控磁盘使用
df -h

📈 性能优化建议

1. 图片压缩

已实现:

  • 头像:最大 1MB1024x1024
  • 照片:最大 2MB1920x1920

2. 浏览器缓存

Next.js 自动处理 public 目录的缓存

3. CDN 加速(可选)

location /uploads/ {
    alias /path/to/app/public/uploads/;
    expires 30d;
    add_header Cache-Control "public, immutable";
}

4. 懒加载

<img 
  src={avatarUrl} 
  loading="lazy"
  alt="头像"
/>

测试清单

  • 头像上传功能
  • 头像显示功能
  • 照片墙上传
  • 照片墙显示
  • 成员列表头像
  • 成员详情头像
  • 家族树节点头像
  • D3 组织图头像
  • 成员表单头像上传
  • 向后兼容性

🎉 迁移完成

所有图片存储已成功从 IndexedDB 迁移到服务器文件系统!

主要改进

  1. 跨设备访问
  2. 数据持久化
  3. 多用户协作
  4. 更好的性能
  5. 易于备份和管理

下一步

  • 测试所有功能
  • 迁移现有数据(如果有)
  • 监控磁盘使用
  • 考虑添加 CDN(可选)