Supabase图片处理 #
一、图片处理概述 #
1.1 内置图片处理 #
text
Supabase图片处理功能
├── 图片缩放
├── 图片裁剪
├── 格式转换
├── 质量调整
├── 水印添加
└── 自动优化
1.2 处理方式 #
text
图片处理方式
├── URL参数处理
│ └── 在URL中添加参数
│
└── 实时处理
└── 请求时即时处理
二、基础图片转换 #
2.1 获取图片URL #
typescript
// 获取原始图片URL
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg')
// 原始URL
// https://xxx.supabase.co/storage/v1/object/public/images/photo.jpg
2.2 图片缩放 #
typescript
// 使用transform选项
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 500,
height: 300,
}
})
// 生成的URL包含转换参数
// https://xxx.supabase.co/storage/v1/render/image/public/images/photo.jpg?width=500&height=300
2.3 调整质量 #
typescript
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
quality: 80, // 1-100
}
})
2.4 格式转换 #
typescript
// 转换为WebP格式
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
format: 'webp',
quality: 80,
}
})
// 支持的格式
// - origin: 保持原格式
// - avif: AVIF格式
// - webp: WebP格式
// - jpg: JPEG格式
// - png: PNG格式
三、缩放模式 #
3.1 按宽度缩放 #
typescript
// 固定宽度,高度自动
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 800,
}
})
3.2 按高度缩放 #
typescript
// 固定高度,宽度自动
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
height: 600,
}
})
3.3 固定尺寸 #
typescript
// 固定宽高
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 400,
height: 300,
resize: 'cover', // 覆盖模式
}
})
3.4 缩放模式 #
typescript
// cover: 覆盖,可能裁剪
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 400,
height: 300,
resize: 'cover',
}
})
// contain: 包含,保持比例
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 400,
height: 300,
resize: 'contain',
}
})
// fill: 填充,可能变形
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 400,
height: 300,
resize: 'fill',
}
})
四、图片裁剪 #
4.1 基础裁剪 #
typescript
// 指定裁剪区域
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 200,
height: 200,
resize: 'cover',
}
})
五、图片组件 #
5.1 响应式图片组件 #
tsx
import { supabase } from '../lib/supabase'
interface ResponsiveImageProps {
bucket: string
path: string
alt: string
sizes?: {
thumbnail?: number
small?: number
medium?: number
large?: number
}
}
export function ResponsiveImage({
bucket,
path,
alt,
sizes = {
thumbnail: 150,
small: 300,
medium: 600,
large: 1200,
}
}: ResponsiveImageProps) {
const baseUrl = supabase.storage.from(bucket).getPublicUrl(path).data.publicUrl
const srcSet = Object.entries(sizes)
.map(([size, width]) => {
const url = supabase.storage
.from(bucket)
.getPublicUrl(path, {
transform: { width }
}).data.publicUrl
return `${url} ${width}w`
})
.join(', ')
return (
<img
src={baseUrl}
srcSet={srcSet}
sizes="(max-width: 600px) 300px, (max-width: 1200px) 600px, 1200px"
alt={alt}
loading="lazy"
/>
)
}
5.2 头像组件 #
tsx
import { supabase } from '../lib/supabase'
interface AvatarProps {
userId: string
size?: 'sm' | 'md' | 'lg'
}
export function Avatar({ userId, size = 'md' }: AvatarProps) {
const sizes = {
sm: 32,
md: 48,
lg: 96,
}
const { data } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}/avatar.jpg`, {
transform: {
width: sizes[size],
height: sizes[size],
resize: 'cover',
}
})
return (
<img
src={data.publicUrl}
alt="Avatar"
width={sizes[size]}
height={sizes[size]}
className="rounded-full"
/>
)
}
5.3 图片画廊组件 #
tsx
import { useState } from 'react'
import { supabase } from '../lib/supabase'
interface GalleryProps {
bucket: string
images: string[]
}
export function Gallery({ bucket, images }: GalleryProps) {
const [selected, setSelected] = useState<string | null>(null)
return (
<div>
<div className="grid grid-cols-4 gap-4">
{images.map((path) => (
<img
key={path}
src={supabase.storage
.from(bucket)
.getPublicUrl(path, {
transform: { width: 200, height: 200, resize: 'cover' }
}).data.publicUrl}
alt=""
onClick={() => setSelected(path)}
className="cursor-pointer"
/>
))}
</div>
{selected && (
<div className="fixed inset-0 bg-black/80 flex items-center justify-center">
<img
src={supabase.storage
.from(bucket)
.getPublicUrl(selected, {
transform: { width: 1200 }
}).data.publicUrl}
alt=""
/>
<button
onClick={() => setSelected(null)}
className="absolute top-4 right-4 text-white"
>
Close
</button>
</div>
)}
</div>
)
}
六、图片上传优化 #
6.1 上传前压缩 #
typescript
async function compressImage(
file: File,
maxWidth: number = 1920,
quality: number = 0.8
): Promise<Blob> {
return new Promise((resolve) => {
const img = new Image()
img.src = URL.createObjectURL(file)
img.onload = () => {
const canvas = document.createElement('canvas')
let { width, height } = img
if (width > maxWidth) {
height = (height * maxWidth) / width
width = maxWidth
}
canvas.width = width
canvas.height = height
const ctx = canvas.getContext('2d')!
ctx.drawImage(img, 0, 0, width, height)
canvas.toBlob(
(blob) => resolve(blob!),
'image/jpeg',
quality
)
URL.revokeObjectURL(img.src)
}
})
}
// 使用
const compressed = await compressImage(file, 1920, 0.8)
await supabase.storage.from('images').upload(path, compressed)
6.2 生成缩略图 #
typescript
async function generateThumbnail(
file: File,
size: number = 200
): Promise<Blob> {
return new Promise((resolve) => {
const img = new Image()
img.src = URL.createObjectURL(file)
img.onload = () => {
const canvas = document.createElement('canvas')
canvas.width = size
canvas.height = size
const ctx = canvas.getContext('2d')!
// 居中裁剪
const minDim = Math.min(img.width, img.height)
const sx = (img.width - minDim) / 2
const sy = (img.height - minDim) / 2
ctx.drawImage(img, sx, sy, minDim, minDim, 0, 0, size, size)
canvas.toBlob(
(blob) => resolve(blob!),
'image/jpeg',
0.8
)
URL.revokeObjectURL(img.src)
}
})
}
七、图片处理最佳实践 #
7.1 性能优化 #
text
图片优化建议
├── 使用WebP格式
├── 适当降低质量(70-80%)
├── 按需生成尺寸
├── 使用CDN缓存
├── 懒加载图片
└── 使用响应式图片
7.2 缓存策略 #
typescript
// 上传时设置缓存
const { data, error } = await supabase.storage
.from('images')
.upload(path, file, {
cacheControl: '31536000', // 1年
})
// 转换图片也会被缓存
const { data } = supabase.storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 800,
quality: 80,
}
})
八、总结 #
图片处理要点:
| 操作 | 参数 |
|---|---|
| 缩放 | width, height |
| 质量调整 | quality (1-100) |
| 格式转换 | format (webp, avif, jpg, png) |
| 缩放模式 | resize (cover, contain, fill) |
下一步,让我们学习实时订阅!
最后更新:2026-03-28