export function fileToDataURL(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = function (event) { resolve(event.target?.result as string); }; reader.onerror = function (error) { reject(error); }; reader.readAsDataURL(file); }); } interface CompressBase64Options { base64: string; maxSizeKB: number; outputFormat?: string; // 'image/jpeg' | 'image/webp' minQuality?: number; // default: 0.1 maxQuality?: number; // default: 1.0 maxIterations?: number; // default: 10 } export async function compressBase64Image(options: CompressBase64Options): Promise { const { base64, maxSizeKB, outputFormat = "image/jpeg", minQuality = 0.1, maxQuality = 1.0, maxIterations = 10, } = options; const img = await new Promise((resolve, reject) => { const image = new Image(); image.crossOrigin = "Anonymous"; image.onload = () => resolve(image); image.onerror = reject; image.src = base64; }); const canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext("2d"); if (!ctx) throw new Error("Could not get canvas context"); ctx.drawImage(img, 0, 0); let minQ = minQuality; let maxQ = maxQuality; let bestBase64 = ""; for (let i = 0; i < maxIterations; i++) { const q = (minQ + maxQ) / 2; const b64 = canvas.toDataURL(outputFormat, q); const size = getBase64ImageSize(b64).kilobytes; if (size > maxSizeKB) { maxQ = q; } else { minQ = q; bestBase64 = b64; } } // If no quality produced a small enough image, return the lowest quality result if (!bestBase64) { bestBase64 = canvas.toDataURL(outputFormat, minQuality); } return bestBase64; } /** * Get the size of a Base64 image string in bytes and kilobytes. * @param base64 - The Base64 image string (with or without data URL prefix). * @returns { bytes: number, kilobytes: number, megabytes: number } */ export function getBase64ImageSize(base64: string): { bytes: number; kilobytes: number; megabytes: number } { // Remove data URL prefix if present const cleanedBase64 = base64.split(",")[1] || base64; // Calculate padding const padding = (cleanedBase64.match(/=+$/) || [""])[0].length; // Calculate size in bytes const bytes = (cleanedBase64.length * 3) / 4 - padding; // Convert to kilobytes const kilobytes = bytes / 1024; // Convert to megabytes (optional) const megabytes = kilobytes / 1024; return { bytes: Math.round(bytes), kilobytes: parseFloat(kilobytes.toFixed(2)), megabytes: parseFloat(megabytes.toFixed(2)), }; }