| export function fileToDataURL(file: File): Promise<string> { |
| 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; |
| minQuality?: number; |
| maxQuality?: number; |
| maxIterations?: number; |
| } |
|
|
| export async function compressBase64Image(options: CompressBase64Options): Promise<string> { |
| const { |
| base64, |
| maxSizeKB, |
| outputFormat = "image/jpeg", |
| minQuality = 0.1, |
| maxQuality = 1.0, |
| maxIterations = 10, |
| } = options; |
|
|
| const img = await new Promise<HTMLImageElement>((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 (!bestBase64) { |
| bestBase64 = canvas.toDataURL(outputFormat, minQuality); |
| } |
|
|
| return bestBase64; |
| } |
|
|
| |
| |
| |
| |
| |
| export function getBase64ImageSize(base64: string): { bytes: number; kilobytes: number; megabytes: number } { |
| |
| const cleanedBase64 = base64.split(",")[1] || base64; |
|
|
| |
| const padding = (cleanedBase64.match(/=+$/) || [""])[0].length; |
|
|
| |
| const bytes = (cleanedBase64.length * 3) / 4 - padding; |
|
|
| |
| const kilobytes = bytes / 1024; |
|
|
| |
| const megabytes = kilobytes / 1024; |
|
|
| return { |
| bytes: Math.round(bytes), |
| kilobytes: parseFloat(kilobytes.toFixed(2)), |
| megabytes: parseFloat(megabytes.toFixed(2)), |
| }; |
| } |
|
|