import imageCompression from "browser-image-compression"; interface ImageSizeVariant { size: "original" | "medium" | "thumbnail"; maxWidth: number; quality: number; suffix: string; } const IMAGE_VARIANTS: ImageSizeVariant[] = [ { size: "thumbnail", maxWidth: 200, quality: 0.8, suffix: "_th" }, { size: "medium", maxWidth: 800, quality: 0.8, suffix: "_md" }, { size: "original", maxWidth: 4096, quality: 0.9, suffix: "" }, ]; interface ResizedImage { variant: ImageSizeVariant; file: File; } /** * Resize an image to all size variants (thumbnail, medium, original) * Returns array of resized File objects */ export async function resizeImage(file: File): Promise { const results: ResizedImage[] = []; for (const variant of IMAGE_VARIANTS) { const options = { maxWidthOrHeight: variant.maxWidth, useWebWorker: true, initialQuality: variant.quality, fileType: "image/jpeg" as const, }; try { const compressedFile = await imageCompression(file, options); // Create new File with variant-specific name const variantFile = new File( [compressedFile], generateVariantFilename(file.name, variant.suffix), { type: "image/jpeg" } ); results.push({ variant, file: variantFile, }); } catch (error) { console.error(`Failed to resize image for ${variant.size}:`, error); throw error; } } return results; } /** * Generate filename with variant suffix * e.g., "photo.png" + "_th" => "photo_th.jpg" */ function generateVariantFilename( originalName: string, suffix: string ): string { const lastDot = originalName.lastIndexOf("."); const baseName = lastDot === -1 ? originalName : originalName.substring(0, lastDot); return `${baseName}${suffix}.jpg`; } /** * Derive S3 key for a specific variant from the base key * e.g., "items/uuid.jpg" + "_th" => "items/uuid_th.jpg" */ export function getVariantKey(baseKey: string, suffix: string): string { if (!suffix) return baseKey; const lastDot = baseKey.lastIndexOf("."); if (lastDot === -1) return `${baseKey}${suffix}`; return `${baseKey.substring(0, lastDot)}${suffix}${baseKey.substring(lastDot)}`; } /** * Get the suffix for a given size */ export function getSizeSuffix( size: "thumbnail" | "medium" | "original" ): string { const variant = IMAGE_VARIANTS.find((v) => v.size === size); return variant?.suffix || ""; }