Estoy trabajando en un proyecto hecho en Symfony2, un framework muy util y que simplifica mucho las cosas, me toco justamente el tema de subir una imagen al servidor.
Para ello ocupe un Bundle llamado VichUploader, el cual permite y facilita mucho el tema de subir imagenes, segui la configuracion basica que requeria y finalmente mi config,yml quedo asi
vich_uploader: db_driver: orm gaufrette: true storage: vich_uploader.storage.gaufrette mappings: product_image: uri_prefix: /images/update upload_destination: product_image_fs namer: vich_uploader.namer_uniqid delete_on_remove: true #El archivo debe ser eliminado del sistema de archivos cuando se quita la entidad delete_on_update: true #el archivo debe ser eliminado del sistema de archivos cuando el archivo se sustituye por otra
Una vez funcionando sin errores, hice la respectiva prueba de subir una imagen, en este momento me aparecio un lindo error
Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)
La solucion a esto es en teoria muy sencilla, solo deberiamos habilitar la extension «php_infofile» y listo
extension=php_fileinfo.dll
Pero… que sucede si tenemos nuestra pagina web alojada en un Hosting que NO tiene habilitada esta extension? pues tenemos dos soluciones
- buscamos otro hosting
- Intentamos otra manera de subir una imagen
En mi caso tuve que ocupar la segunda opcion 🙁 y aqui fue donde vino una linda odisea que por lo menos logre encontrar una solucion
Primero quitaremos la validacion existente en nuestra entidad para las extensiones mimes
mimeTypes = {"image/jpg", "image/jpeg", "image/png"},
Debido a que lo reemplazaremos por una validacion con Constraint, por lo tanto nos quedaria asi
use AN\PortalBundle\Validator\Constraints as ANAssert; class ArchivosCandidato { /** * @Assert\File( * maxSize="3M" * ) * @Vich\UploadableField(mapping="product_image", fileNameProperty="imageName") * * @var File $image * @ANAssert\ContainsMimeType */ protected $file; //... }
Notese que solo estamos validando el tamaño del archivo y mediante el «ContainsMimeType», validaremos la extension
Creamos nuestro archivo «ContainsMimeType.php»
namespace AN\PortalBundle\Validator\Constraints; use Symfony\Component\Validator\Constraint; /** * @Annotation */ class ContainsMimeType extends Constraint { public $message = 'errores.ContainsMimeType'; }
Creamos nuestro archivo «ContainsMimeTypeValidator.php» el cual contendra la logica y las extensiones permitidas, esto lo puedes pasar a un parametro en tu archivo de configuracion, pero eso lo vera cada uno
namespace AN\PortalBundle\Validator\Constraints; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; class ContainsMimeTypeValidator extends ConstraintValidator { public function validate($file, Constraint $constraint) { //Default $return = false; //Tipos permitidos $listType = "image/png,image/jpeg,image/pjpeg"; try { //Tipo actual $type = $file->getClientMimeType(); //Si esta dentro del listado, seteamos true para que no muestra una excepcion if(substr_count($listType, $type) > 0) $return = true; } catch(\Exception $e) {} //Definimos si mostramos o no el mensaje de error o excepcion if($return == false) { $this->context->addViolation($constraint->message, array( '{{ types }}' => '"'.$listType .'"', )); } } }
La idea es bastante simple, setea una variable de retorno, y si no encuentra la extension u ocurre algun error agrega un constraint con el error
Con esto ya estariamos validando la extension de nuestro archivo, pero si estamos ocupando «vich» seguiriamos teniendo problemas, esto debido a que si ocupamos
namer: vich_uploader.namer_uniqid
Esta funcion aun sigue ocupando «php_fileinfo», por lo tanto tenemos dos soluciones:
- Ocupar «vich_uploader.namer_origname»
- Clonar o crear nuestro propio «vich_uploader.namer_uniqid»
Yo queria seguir teniendo un ID unico por cada imagen, asi que ocupe la segunda solucion propuesta, y clone el «vich_uploader.namer_uniqid», si tu optas por la primera opcion, lo que sigue a continuacion no deberas tomarlo en cuenta.
Clonando «vich_uploader.namer_uniqid»
En mi caso tengo las rutas en parameters.yml para cambiarlos mas facilmente
#Crea nombre unico para archivos con VICH gestor_vich_namer_uniqid.class: AN\PortalBundle\Services\Naming\UniqidNamer
En nuestro archivo «services.yml»
#Creamos un Servicio que creara un nombre Unico para subir archivos con VICH an.gestor.vichnameruniqid: class: "%gestor_vich_namer_uniqid.class%"
En config.yml indicamos que estamos llamando a un nuevo namer
vich_uploader: db_driver: orm gaufrette: true storage: vich_uploader.storage.gaufrette mappings: product_image: uri_prefix: %vich_url_images%%locale% upload_destination: product_image_fs namer: an.gestor.vichnameruniqid
Nosete que aqui solo cambia el «namer:» lo demas sigue tal cual lo tienes
Por ultimo, creamos nuestro archivo «UniqidNamer.php» (ocupe el mismo nombre porque es casi lo mismo)
namespace AN\PortalBundle\Services\Naming; use Vich\UploaderBundle\Naming\NamerInterface; /** * UniqidNamer * */ class UniqidNamer implements NamerInterface { /** * {@inheritDoc} */ public function name($obj, $field) { $refObj = new \ReflectionObject($obj); $refProp = $refObj->getProperty($field); $refProp->setAccessible(true); $file = $refProp->getValue($obj); $name = uniqid(); //Seteamos la extension $extension = strtolower($file->getClientOriginalExtension()); if (strlen($extension) > 0) { $name = sprintf('%s.%s', $name, $extension); } return $name; } }
Aqui lo unico diferente en comparacion con el archivo original es en la forma de obtener la extension, como ya validamos con nuestro Constraint que la extension fuera correcta, aqui solo ocupamos la extension original
Eso seria todo, si es que no se me olvido nada
El post oficial en donde deje esto fue aqui
Atte, Ariel