Skip to content

Commit 24fe017

Browse files
28nitin07claude
andcommitted
perf(frontend): compress wardrobe images before Supabase upload
Full-resolution camera blobs (1-2 MB at quality 0.92) were being uploaded directly, making save noticeably slow on mobile. compressForStorage() resizes to a max 800px longest edge at JPEG quality 0.75, typically producing 50-150 KB. The full-res blob is still sent to the ML backend for inference — only the storage copy is compressed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d2db470 commit 24fe017

1 file changed

Lines changed: 25 additions & 3 deletions

File tree

frontend/src/utils/storage.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,37 @@ export async function deleteWardrobeItem(id) {
3636
if (error) throw error
3737
}
3838

39+
// Resize + compress a blob to a thumbnail suitable for wardrobe card display.
40+
// Keeps the longest edge at maxPx, JPEG quality 0.75 — typically 50–150 KB.
41+
async function compressForStorage(blob, maxPx = 800) {
42+
return new Promise((resolve) => {
43+
const img = new Image()
44+
const url = URL.createObjectURL(blob)
45+
img.onload = () => {
46+
URL.revokeObjectURL(url)
47+
const { naturalWidth: w, naturalHeight: h } = img
48+
const scale = Math.min(1, maxPx / Math.max(w, h))
49+
const canvas = document.createElement('canvas')
50+
canvas.width = Math.round(w * scale)
51+
canvas.height = Math.round(h * scale)
52+
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height)
53+
canvas.toBlob((compressed) => resolve(compressed || blob), 'image/jpeg', 0.75)
54+
}
55+
img.onerror = () => { URL.revokeObjectURL(url); resolve(blob) }
56+
img.src = url
57+
})
58+
}
59+
3960
// Upload image blob to the 'wardrobe-images' storage bucket.
61+
// Compresses to a thumbnail first to keep uploads fast on mobile.
4062
// Returns public URL on success, null if bucket doesn't exist or upload fails.
4163
export async function uploadWardrobeImage(userId, blob) {
4264
try {
43-
const ext = blob.type === 'image/png' ? 'png' : 'jpg'
44-
const path = `${userId}/${Date.now()}.${ext}`
65+
const compressed = await compressForStorage(blob)
66+
const path = `${userId}/${Date.now()}.jpg`
4567
const { error: uploadError } = await supabase.storage
4668
.from('wardrobe-images')
47-
.upload(path, blob, { contentType: blob.type || 'image/jpeg', upsert: false })
69+
.upload(path, compressed, { contentType: 'image/jpeg', upsert: false })
4870
if (uploadError) throw uploadError
4971
const { data } = supabase.storage.from('wardrobe-images').getPublicUrl(path)
5072
return data.publicUrl

0 commit comments

Comments
 (0)