From edcefbe67c395bab74a7705c85fb586819fbe84e Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:26:36 -0500 Subject: [PATCH] fix: comprimir imagen pegada hasta <250KB con calidad progresiva MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenSSL 3.x en contenedor hace renegociacion TLS con Facebook >~300KB causando errno 55. Forzar imagen a quedar bajo 250KB: - Escalar a max 1920px si es más grande - Intentar JPEG q=0.85, 0.70, 0.55, 0.40, 0.25 hasta pasar el umbral - JPEG/WebP ya bajo 250KB se pasan sin modificar --- conversations.php | 56 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/conversations.php b/conversations.php index f7fda01..be3b9de 100644 --- a/conversations.php +++ b/conversations.php @@ -2200,14 +2200,16 @@ if (!isset($_SESSION['user_id'])) { const cd = (e.clipboardData || window.clipboardData); if (!cd) return; - // ─── Comprimir imagen pegada a JPEG vía Canvas ──────────────────────── - // Las imágenes del portapapeles llegan como PNG sin comprimir (1-5 MB). - // Facebook resetea la conexión TCP al recibir payloads grandes desde este - // contenedor → errno 55. Comprimir a JPEG 0.85 reduce el tamaño ~10x. - // Si el tipo ya es JPEG o WebP, se mantiene sin recomprimir. + // ─── Comprimir imagen pegada a JPEG ───────────────────────────────────── + // El contenedor usa OpenSSL 3.x que hace renegociación TLS con Facebook + // para payloads >~300KB → errno 55 "Send failure". Solución: forzar + // que la imagen quede bajo 250KB mediante calidad progresiva + escala. + const TARGET_BYTES = 250 * 1024; // 250KB + const MAX_DIM = 1920; // máximo ancho/alto antes de escalar + const compressToJpeg = (blob) => new Promise((resolve) => { - // Si ya es JPEG o WebP, no recomprimir (ya están comprimidos) - if (blob.type === 'image/jpeg' || blob.type === 'image/webp') { + // Si ya es pequeño JPEG/WebP, no recomprimir + if ((blob.type === 'image/jpeg' || blob.type === 'image/webp') && blob.size <= TARGET_BYTES) { resolve(blob); return; } @@ -2215,17 +2217,39 @@ if (!isset($_SESSION['user_id'])) { const img = new Image(); img.onload = () => { URL.revokeObjectURL(url); + + // Escalar si la imagen es más grande que MAX_DIM + let w = img.naturalWidth, h = img.naturalHeight; + if (w > MAX_DIM || h > MAX_DIM) { + const ratio = Math.min(MAX_DIM / w, MAX_DIM / h); + w = Math.round(w * ratio); + h = Math.round(h * ratio); + } + const canvas = document.createElement('canvas'); - canvas.width = img.naturalWidth; - canvas.height = img.naturalHeight; + canvas.width = w; canvas.height = h; const ctx = canvas.getContext('2d'); - // Fondo blanco para transparencias PNG - ctx.fillStyle = '#ffffff'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0); - canvas.toBlob((jpegBlob) => { - resolve(jpegBlob || blob); // fallback al original si toBlob falla - }, 'image/jpeg', 0.85); + ctx.fillStyle = '#ffffff'; // fondo blanco para PNGs con transparencia + ctx.fillRect(0, 0, w, h); + ctx.drawImage(img, 0, 0, w, h); + + // Calidades a intentar hasta quedar bajo TARGET_BYTES + const qualities = [0.85, 0.70, 0.55, 0.40, 0.25]; + let idx = 0; + const tryNext = () => { + if (idx >= qualities.length) { resolve(blob); return; } // fallback + canvas.toBlob((b) => { + if (!b) { resolve(blob); return; } + if (b.size <= TARGET_BYTES || idx === qualities.length - 1) { + console.log(`Paste compress: ${(blob.size/1024).toFixed(0)}KB → ${(b.size/1024).toFixed(0)}KB (q=${qualities[idx]} ${w}×${h}px)`); + resolve(b); + } else { + idx++; + tryNext(); + } + }, 'image/jpeg', qualities[idx]); + }; + tryNext(); }; img.onerror = () => { URL.revokeObjectURL(url); resolve(blob); }; img.src = url;