6.2 KiB
6.2 KiB
图片存储解决方案
🚨 当前问题
项目目前使用 IndexedDB 存储图片,这存在以下问题:
1. 本地存储限制
- IndexedDB 是浏览器本地存储
- 数据只存在用户的浏览器中
- 无法跨设备访问
- 清除浏览器数据会丢失所有图片
2. 无法共享
- 其他用户看不到上传的图片
- 协作者无法访问
- 不适合多用户系统
3. 存储容量限制
- 浏览器对 IndexedDB 有容量限制
- 大量图片会占用用户设备空间
- 可能导致性能问题
✅ 推荐解决方案
方案一:云存储服务(推荐)
使用专业的云存储服务,如:
国内服务
- 阿里云 OSS
- 腾讯云 COS
- 七牛云
- 又拍云
国际服务
- AWS S3
- Google Cloud Storage
- Azure Blob Storage
- Cloudflare R2
实现步骤
- 注册云存储服务
- 创建存储桶(Bucket)
- 获取 API 密钥
- 创建上传 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 })
}
- 前端调用
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
- 📋 需要统一迁移所有图片存储
🎯 下一步
- 选择云存储服务
- 创建上传 API
- 修改前端上传逻辑
- 迁移现有数据
- 测试验证
- 部署上线