ComurImageBundle icon indicating copy to clipboard operation
ComurImageBundle copied to clipboard

Delete unused images

Open comur opened this issue 12 years ago • 8 comments

Have to find a way to delete images when they are not used anymore.

2 possibilities:

  1. Add a listener on preUpdate and remove old images
  2. Add a trigger on image change in the widget and call an ajax route to delete old images

Any way, we have to create a service to get all image types (cropped, thumbs) related to an image.

comur avatar Feb 13 '14 09:02 comur

Hi.

It's needed this. I'm detecting some issues:

1.- When I create a new item and I upload a image, automatically the file is upload to "[defined path]" and "[defined path]/thumbnails" folder with a hash. And I have no original image defined in the config.

2.- If I crop the image and continue, the cropped image is uploaded automatically to "[defined path]/cropped" and "[defined path]/cropped/thumbnails" folder with a hash, wich is different to full image hash.

3.- If I flush the new item, all it's ok. The files are there. But in the database I only have the cropped filename with the step 2 hash.

4.- If I cancel the new item, the images have been uploaded anyway.

Then, I can't imagine how to handle the images deletion. My knowledge of symfony is limited, but I'm trying to handle this and I'm frustrated.

I hope you will deal with this soon. I don't know how to resolve it :-(

Tersoal avatar Feb 28 '15 00:02 Tersoal

Hi @Tersoal

I have some thoughts about this issue.

First, you can save original file name in another field if you want to delete it when your entity is removed (be careful, you will no longer see this image in "already uploaded" images selection). To do this, you can pass a parameter to your field : https://github.com/comur/ComurImageBundle#saveoriginal-optional

Then in your entity you need to use http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#preremove function to delete images uploaded / cropped by this entity.

Tell me if you are able to achieve that.

comur avatar Mar 02 '15 14:03 comur

Thanks!

Let me see...

Tersoal avatar Mar 02 '15 19:03 Tersoal

Well, it does not work.

I have defined my image field with 'saveOriginal' => false (or you can delete this as it's by default). The image file is upload by ajax to the server anyway, just when the image is selected to upload.

Preremove lyfecycle callback does not work because the defined field isn't a file, but string instead.

I'm updating my form, entity and controller to delete files. I'll put here, and it should be included in the doc for reference to new users.

Tersoal avatar Mar 05 '15 17:03 Tersoal

Send me your code and i will show you how to do it. I have to include a real example in the project

comur avatar Mar 06 '15 07:03 comur

I'll put here. Is up to you how include it in the docs.

I see other problems that I have to confirm in my tests.

Tersoal avatar Mar 06 '15 08:03 Tersoal

Hi.

I have resolved the old image files deletion with a service. The problem is that it must be done for all entities that have a comur image field, and I have tried to do in the bundle, but I don't know how identify the image fields in classmetada. My knowledge of symfony is not sufficient.

  1. Entity and form field: as doc says, but originalImage is mandatory for deleting uploaded image (I don't know why does occur it if I set the originalimage to false).

  2. Create service definition:

    acme.subscriber.hello: class: Acme\HelloBundle\Service\HelloSubscriber tags: - {name: doctrine.event_subscriber } arguments: [%comur_image.cropped_image_dir%, %comur_image.thumbs_dir%, "@service_container", %comur_image.web_dirname%, %comur_image.gallery_dir%]

  3. Create service: all reference to tempNewOriginalImage must be commented if you don't want to delete the original image.

tempImage = null; $this->tempOriginalImage = null; $this->tempNewOriginalImage = null; ``` $this->croppedDir = $croppedDir; $this->thumbsDir = $thumbsDir; $this->webDir = $container->get('kernel')->getRootdir().'/../' . $webDirName; $this->galleryDir = $galleryDir; ``` } public function getSubscribedEvents() { return array( 'prePersist', 'postPersist', 'preUpdate', 'postUpdate', ); } public function prePersist(LifeCycleEventArgs $args) { $entity = $args->getObject(); ``` if ($entity instanceof Hello) { $this->tempNewOriginalImage = $entity->getOriginalImage(); $entity->setOriginalImage(null); } ``` } public function postPersist(LifeCycleEventArgs $args) { if (null === $this->tempNewOriginalImage) { return; } ``` $entity = $args->getObject(); $this->removeNewOriginalImage($entity); ``` } public function preUpdate(LifeCycleEventArgs $args) { $entity = $args->getObject(); ``` if ($entity instanceof Hello) { if ($args->hasChangedField('image')) { $this->tempImage = $args->getOldValue('image'); $this->tempOriginalImage = $args->getOldValue('originalImage'); $this->tempNewOriginalImage = $args->getNewValue('originalImage'); $entity->setOriginalImage(null); } } ``` } public function postUpdate(LifeCycleEventArgs $args) { if (null === $this->tempImage && null === $this->tempOriginalImage && null === $this->tempNewOriginalImage) { return; } ``` $entity = $args->getObject(); if (null !== $this->tempImage) { $imageName = explode('/', $this->tempImage); $imageName = end($imageName); // Delete cropped image $this->removeFile($this->webDir.'/'.$entity->getUploadDir().'/'.$this->croppedDir.'/'.$imageName); // Delete cropped image thumbnail $croppedImageNames = glob($this->webDir.'/'.$entity->getUploadDir().'/'.$this->croppedDir.'/'.$this->thumbsDir.'/*' . $imageName); foreach ($croppedImageNames as $croppedImageName) { unlink($croppedImageName); } $this->tempImage = null; } if (null !== $this->tempOriginalImage) { $this->removeNewOriginalImage($entity); } if (null !== $this->tempNewOriginalImage) { $this->removeNewOriginalImage($entity); } ``` } public function removeOriginalImage($entity) { // Delete new original image and it's thumbnail $this->removeFile($this->webDir.'/'.$entity->getUploadDir().'/'.$this->tempOriginalImage); $this->removeFile($this->webDir.'/'.$entity->getUploadDir().'/'.$this->thumbsDir.'/'.$this->tempOriginalImage); ``` $this->tempOriginalImage = null; ``` } public function removeNewOriginalImage($entity) { // Delete new original image and it's thumbnail $this->removeFile($this->webDir.'/'.$entity->getUploadDir().'/'.$this->tempNewOriginalImage); $this->removeFile($this->webDir.'/'.$entity->getUploadDir().'/'.$this->thumbsDir.'/'.$this->tempNewOriginalImage); ``` $this->tempNewOriginalImage = null; ``` } public function removeFile($file) { if (file_exists($file)) { unlink($file); } } } TO-DO: - If I cancel new or edit entity, the images are uploaded and the can't be deleted. It's possible to create a synchronous ajax call in window.beforeunload event, but it can cause problems in the browser if server don't respond for whatever reason. As well, I've a script copied from Sonata admin, wich allows to the user cancel redirection if form has been modified. Thus, the ajax call must not be done if the user cancels the action, and it's not possible with other beforeunload action in the comur bundle. Same occurs in the unload event. - I will create a cron job to delete uploaded images periodically. It's the only solution that comes to my mind.

Tersoal avatar Mar 07 '15 08:03 Tersoal

Hi,

Thanks for this great job, I will take some time to check it.

comur avatar Mar 09 '15 13:03 comur