fix: comprimir imagen pegada hasta <250KB con calidad progresiva

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
This commit is contained in:
Lizandro Guarnizo
2026-02-21 12:26:36 -05:00
parent dd6dd2e1f4
commit edcefbe67c
+40 -16
View File
@@ -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;