fix: comprimir imagen pegada a JPEG antes de subir a WhatsApp

Imagen PNG del portapapeles puede ser 1-5MB sin comprimir.
Facebook resetea TCP (errno 55) al recibir payloads grandes desde el
contenedor con OpenSSL 3.x. Solución: convertir PNG→JPEG 0.85 vía Canvas
en el navegador antes de subir → reduce tamaño ~10x → upload exitoso.
JPEG/WebP ya comprimidos se pasan sin modificar.
This commit is contained in:
Lizandro Guarnizo
2026-02-21 12:20:02 -05:00
parent a716cd187a
commit dd6dd2e1f4
+51 -19
View File
@@ -2200,6 +2200,37 @@ 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.
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') {
resolve(blob);
return;
}
const url = URL.createObjectURL(blob);
const img = new Image();
img.onload = () => {
URL.revokeObjectURL(url);
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
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);
};
img.onerror = () => { URL.revokeObjectURL(url); resolve(blob); };
img.src = url;
});
// Helper: convertir base64 a Blob
const b64ToBlob = (b64, mime) => {
const bytes = atob(b64);
@@ -2209,17 +2240,26 @@ if (!isset($_SESSION['user_id'])) {
return new Blob([arr], { type: mime });
};
// Helper: procesar un blob de imagen (comprimir + preview)
const handleImageBlob = async (blob, originalName) => {
const compressed = await compressToJpeg(blob);
const name = (originalName || 'pasted_image').replace(/\.[^.]+$/, '') + '.jpg';
const file = new File([compressed], name, { type: 'image/jpeg' });
const max = this.getMaxFileSize(file.type);
if (file.size > max) {
alert('El archivo es demasiado grande. Máximo: ' + (max / 1024 / 1024).toFixed(0) + 'MB');
return;
}
console.log(`Imagen pegada comprimida: ${(blob.size/1024).toFixed(0)}KB → ${(file.size/1024).toFixed(0)}KB (JPEG 0.85)`);
this.showMediaPreview(file);
};
// 1) Si hay archivos directos en clipboard (Chrome/Edge)
if (cd.files && cd.files.length) {
for (const file of cd.files) {
if (file && file.type && file.type.startsWith('image/')) {
e.preventDefault && e.preventDefault();
const max = this.getMaxFileSize(file.type);
if (file.size > max) {
alert('El archivo es demasiado grande. Máximo: ' + (max / 1024 / 1024).toFixed(0) + 'MB');
return;
}
this.showMediaPreview(file);
handleImageBlob(file, file.name);
return;
}
}
@@ -2231,11 +2271,9 @@ if (!isset($_SESSION['user_id'])) {
try {
if (item.kind === 'file' && item.type && item.type.startsWith('image/')) {
e.preventDefault && e.preventDefault();
const file = item.getAsFile();
if (file) {
const max = this.getMaxFileSize(file.type);
if (file.size > max) { alert('El archivo es demasiado grande. Máximo: ' + (max / 1024 / 1024).toFixed(0) + 'MB'); return; }
this.showMediaPreview(file);
const blob = item.getAsFile();
if (blob) {
handleImageBlob(blob, blob.name || 'pasted_image.png');
return;
}
} else if (item.kind === 'string' && (item.type === 'text/html' || item.type === 'text/plain')) {
@@ -2244,14 +2282,8 @@ if (!isset($_SESSION['user_id'])) {
const m = s && s.match ? s.match(/src=["']data:(image\/[^;]+);base64,([^"']+)["']/i) : null;
if (m) {
try {
const mime = m[1];
const b64 = m[2];
const blob = b64ToBlob(b64, mime);
const ext = (mime.split('/')[1] || 'png').split('+')[0];
const file = new File([blob], 'pasted_image.' + ext, { type: mime });
const max = this.getMaxFileSize(file.type);
if (file.size > max) { alert('El archivo es demasiado grande. Máximo: ' + (max / 1024 / 1024).toFixed(0) + 'MB'); return; }
this.showMediaPreview(file);
const blob = b64ToBlob(m[2], m[1]);
handleImageBlob(blob, 'pasted_image.png');
} catch (err) { console.warn('paste image convert failed', err); }
}
});