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

6.2 KiB
Raw Blame History

图片存储解决方案

🚨 当前问题

项目目前使用 IndexedDB 存储图片,这存在以下问题:

1. 本地存储限制

  • IndexedDB 是浏览器本地存储
  • 数据只存在用户的浏览器中
  • 无法跨设备访问
  • 清除浏览器数据会丢失所有图片

2. 无法共享

  • 其他用户看不到上传的图片
  • 协作者无法访问
  • 不适合多用户系统

3. 存储容量限制

  • 浏览器对 IndexedDB 有容量限制
  • 大量图片会占用用户设备空间
  • 可能导致性能问题

推荐解决方案

方案一:云存储服务(推荐)

使用专业的云存储服务,如:

国内服务

  • 阿里云 OSS
  • 腾讯云 COS
  • 七牛云
  • 又拍云

国际服务

  • AWS S3
  • Google Cloud Storage
  • Azure Blob Storage
  • Cloudflare R2

实现步骤

  1. 注册云存储服务
  2. 创建存储桶(Bucket
  3. 获取 API 密钥
  4. 创建上传 API
// app/api/upload/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'

const s3Client = new S3Client({
  region: process.env.AWS_REGION!,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
})

export async function POST(req: NextRequest) {
  const session = await getServerSession(authOptions)
  if (!session?.user?.id) {
    return NextResponse.json({ error: '未授权' }, { status: 401 })
  }

  const formData = await req.formData()
  const file = formData.get('file') as File
  
  if (!file) {
    return NextResponse.json({ error: '没有文件' }, { status: 400 })
  }

  // 生成唯一文件名
  const fileName = `avatars/${Date.now()}-${file.name}`
  
  // 上传到 S3
  const buffer = Buffer.from(await file.arrayBuffer())
  await s3Client.send(new PutObjectCommand({
    Bucket: process.env.AWS_BUCKET_NAME!,
    Key: fileName,
    Body: buffer,
    ContentType: file.type,
  }))

  // 返回 URL
  const url = `https://${process.env.AWS_BUCKET_NAME}.s3.${process.env.AWS_REGION}.amazonaws.com/${fileName}`
  
  return NextResponse.json({ url })
}
  1. 前端调用
const handleAvatarUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
  const file = event.target.files?.[0]
  if (!file) return

  try {
    setIsUploadingAvatar(true)
    
    // 上传到云存储
    const formData = new FormData()
    formData.append('file', file)
    
    const response = await fetch('/api/upload', {
      method: 'POST',
      body: formData,
    })
    
    const { url } = await response.json()
    
    // 更新成员头像 URL
    await updateMember(member.id, {
      avatarUrl: url,
    })
    
    setAvatarBlobUrl(url)
  } catch (error) {
    console.error('上传头像失败:', error)
  } finally {
    setIsUploadingAvatar(false)
  }
}

方案二:服务器文件系统

将图片存储在服务器的文件系统中。

优点

  • 不需要额外的云服务
  • 成本较低

缺点

  • 需要管理服务器存储空间
  • 扩展性较差
  • 需要处理备份

实现步骤

// app/api/upload/route.ts
import { writeFile } from 'fs/promises'
import { join } from 'path'

export async function POST(req: NextRequest) {
  const formData = await req.formData()
  const file = formData.get('file') as File
  
  const bytes = await file.arrayBuffer()
  const buffer = Buffer.from(bytes)
  
  // 保存到 public/uploads 目录
  const fileName = `${Date.now()}-${file.name}`
  const path = join(process.cwd(), 'public', 'uploads', fileName)
  await writeFile(path, buffer)
  
  // 返回相对 URL
  const url = `/uploads/${fileName}`
  return NextResponse.json({ url })
}

方案三:Base64 编码(不推荐)

将图片转换为 Base64 字符串存储在数据库中。

缺点

  • 数据库体积急剧增大
  • 查询性能下降
  • 不适合大图片
  • 传输效率低

不推荐用于生产环境!

📊 方案对比

方案 成本 性能 可靠性 扩展性 推荐度
云存储
服务器文件系统
IndexedDB
Base64

🔧 迁移步骤

1. 选择云存储服务

推荐:阿里云 OSS(国内)或 AWS S3(国际)

2. 配置环境变量

# .env.local
AWS_REGION=ap-southeast-1
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_BUCKET_NAME=your_bucket_name

3. 安装依赖

npm install @aws-sdk/client-s3
# 或
npm install ali-oss  # 阿里云

4. 创建上传 API

参考上面的代码示例

5. 修改前端代码

  • 移除 IndexedDB 相关代码
  • 改为调用上传 API
  • 使用返回的 URL

6. 数据迁移

  • 将现有的 IndexedDB 图片迁移到云存储
  • 更新数据库中的 URL

💡 最佳实践

1. 图片压缩

上传前压缩图片以节省存储和带宽:

import imageCompression from 'browser-image-compression'

const compressedFile = await imageCompression(file, {
  maxSizeMB: 1,
  maxWidthOrHeight: 1920,
})

2. 图片格式

  • 头像:建议 JPEG/WebP,尺寸 500x500
  • 照片墙:建议 JPEG/WebP,尺寸 1920x1080

3. CDN 加速

使用 CDN 加速图片访问:

  • 阿里云 OSS + CDN
  • AWS S3 + CloudFront
  • Cloudflare R2(自带 CDN

4. 安全性

  • 使用签名 URL 控制访问
  • 设置合理的 CORS 策略
  • 限制上传文件大小和类型

5. 成本优化

  • 定期清理未使用的图片
  • 使用生命周期策略自动归档
  • 选择合适的存储类型

📝 当前状态

  • 头像上传功能已实现(使用 IndexedDB)
  • ⚠️ 需要迁移到云存储
  • 📋 照片墙功能也使用 IndexedDB
  • 📋 需要统一迁移所有图片存储

🎯 下一步

  1. 选择云存储服务
  2. 创建上传 API
  3. 修改前端上传逻辑
  4. 迁移现有数据
  5. 测试验证
  6. 部署上线