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:
+51
-19
@@ -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); }
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user