🛠️ Actualización técnica: validación y subida de imágenes + controlador seguro en boilerplateproducts
🖼️
En este cambio abordamos varios problemas prácticos que afectaban la subida de imágenes y la robustez del endpoint admin/products/save
. Objetivos principales:
- ✅ Permitir JPG/JPEG además de PNG (y manejar HEIC de iPhone).
- ✅ Soportar subida desde móvil (cámara/galería).
- ✅ Validar tipo y tamaño en cliente y servidor (pasamos de 2 MB a 5 MB).
- ✅ Enviar la imagen con
FormData
solo si existe y evitar errores por selectores incorrectos. - ✅ Mejorar el controlador en CodeIgniter 4: validaciones, rutas absolutas, manejo seguro de archivos, respuestas JSON y logging. 📦
1 — Problema inicial 🐞
- El JS validaba solo
image/png
, por lo que bloqueaba.jpg/.jpeg
. ❌ - Selectores inconsistentes (
.imagenProducto
vs#imagenProducto
) => riesgo deundefined
. ⚠️ - El controlador PHP aceptaba únicamente PNG, hacía
var_dump
, usaba rutas relativas y agarraba excepciones equivocadas. 🧨 - Tamaño máximo 2 MB; se solicitó ampliar a 5 MB. 📈
- No había protocolo JSON consistente entre frontend y backend. 🧩
2 — Cambios en el frontend (JS) 📱
Qué se cambió (resumen)
- Validación de tipos permitidos:
image/png
,image/jpeg
,image/jpg
. ✅ - Detección y rechazo de HEIC (iPhone) con aviso o rechazo según preferencia. 🚫🧾
- Límite de tamaño subido: 5 MB (
maxSize = 5 * 1024 * 1024
). 📏 - Uso de
FormData
solamente si el archivo existe. 🎒 - Unificación de selectores: usar
id="imagenProducto"
y mantenerclass
para preview. 🧭 e.preventDefault()
y bloqueo/rehabilitación del botón concomplete
. 🔒➡️🔓dataType: "json"
en AJAX con fallback a texto plano para compatibilidad. 🔁- Mensajes Toast claros y amigables. 🔔
Snippet — parte clave del JS (resumido)
// ... (código resumido; validar tipo, tamaño 5MB, añadir FormData solo si existe)
var maxSize = 5 * 1024 * 1024; // 5 MB
if (imagenProducto.size > maxSize) {
Toast.fire({ icon: 'error', title: "La imagen pesa más de 5 MB." });
$btn.removeAttr("disabled");
return;
}
3 — Cambios en el backend (Controlador CodeIgniter 4) ⚙️
Objetivos del backend
- Aceptar PNG y JPG/JPEG. 🟢
- Validar tamaño (5 MB). 📦
- Evitar
var_dump
en producción. 🚫 - Usar
FCPATH
para mover archivos apublic/images/products
. 📁 - Crear carpeta si no existe. 🧱
- Devolver JSON con estado
ok|error
ymessage
. 💬 - Borrar la imagen anterior de forma segura al actualizar. 🧹
- Manejar excepciones generales correctamente. 🔒
Snippet — método save()
resumido
// Validación de archivo
$maxSize = 5 * 1024 * 1024;
$allowed = ['png','jpg','jpeg'];
if ($imagenProducto && $imagenProducto->isValid()) {
$ext = strtolower($imagenProducto->getClientExtension() ?: '');
if (!in_array($ext, $allowed)) {
return $this->response->setStatusCode(415)->setJSON(['status'=>'error','message'=>lang('empresas.imageExtensionIncorrect')]);
}
if ($imagenProducto->getSize() > $maxSize) {
return $this->response->setStatusCode(413)->setJSON(['status'=>'error','message'=>lang('empresas.imageTooLarge')]);
}
$datos['routeImage'] = $imagenProducto->getRandomName();
}
4 — Configuración del servidor y notas operativas 🖥️
- PHP (
php.ini
): aumentarupload_max_filesize
ypost_max_size
por encima de 5 MB (ej.8M
). 🔧 - Nginx:
client_max_body_size 8M;
. ⚠️ - Permisos:
public/images/products
debe ser escribible por el proceso web (ej.www-data
). 🛡️ - Seguridad: siempre validar MIME real y extensión en el servidor — no confiar en JS. 🔍
- Backups: si las imágenes son críticas, considera mantener copia antes de
unlink
. 💾
5 — Pruebas que hicimos y checklist para QA 🧪✅
Pruebas realizadas:
- Subida desde desktop: JPG, PNG. 💻
- Subida desde móvil: cámara (Android/Chrome) y galería. 📱
- Subida desde iPhone (HEIC) → detectado y rechazado con mensaje. 🍏🚫
- Reemplazo de imagen de producto: antiguo archivo borrado y nuevo movido. 🔁
- Validación de tamaños: >5MB rechazado, <5MB aceptado. ⚖️
- Manejo de respuesta JSON en frontend y fallback a texto. 🔄
Checklist para QA / PR:
- [ ] ✅ El input tiene
id="imagenProducto"
yaccept="image/png, image/jpeg"
(oaccept="image/*"
si se quiere cámara directa). - [ ] ✅ Validación en JS y servidor coincide (5 MB).
- [ ] ✅
images/products
existe y es escribible en staging/prod. - [ ] ✅ Mensajes Toast claros para cada caso (HEIC, tamaño, formato).
- [ ] ✅ Al actualizar producto, la imagen anterior se borra sólo si corresponde.
- [ ] ✅ Tests unitarios / integración pasan (si existen).
- [ ] ✅
composer.lock
no fue tocado por estos cambios (si aplica).
6 — Mensajes de commit / PR recomendados ✍️
Commit:fix(products): accept jpeg/png, validate size 5MB, handle uploads safely
✅
Descripción PR sugerida:
- Frontend: validar JPG/JPEG + PNG, rechazar HEIC, aumentar tamaño a 5MB, enviar FormData solo si existe, unificar selectores y mejorar UX (disable button + toast).
- Backend (CI4): aceptar jpg/png, validar tamaño, usar FCPATH para almacenamiento, crear carpeta si no existe, devolver JSON, borrar imagen anterior de forma segura, mejorar manejo de errores y logging.
Incluye checklist de QA y capturas de la subida desde móvil/desktop.
7 — Próximos pasos recomendados 🚀
- ✂️ Optimizar imágenes: redimensionar/comprimir en cliente o servidor para ahorrar espacio y tiempo de subida.
- ♻️ Soportar HEIC: implementar conversión con
Imagick
o servicio externo si quieres aceptar HEIC automáticamente. - 🧪 Automatizar pruebas: agregar tests para multipart upload y para el flujo de reemplazo/borrado de imágenes.
- 🔁 Pipeline CI/CD: en staging hacer
composer update
si fuera necesario, probar y en producción usarcomposer install
(lockfile).
8 — Conclusión 🎯
Estos cambios hacen la subida de imágenes más robusta, amigable para móviles y segura en el servidor. El frontend ahora maneja errores con mensajes claros y el backend devuelve JSON estructurado para un manejo predecible en la UI. Además, ampliamos el límite a 5 MB y arreglamos detalles clásicos como rutas relativas y selectores inconsistentes. ✅
Deja un comentario