-
-
Save acataluddi/e5887529981f67e03eb3ffd53a577bef to your computer and use it in GitHub Desktop.
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
| <html xmlns="http://www.w3.org/1999/xhtml"> | |
| <head> | |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
| <title>Remove Magento's orphan images web console</title> | |
| <link href='https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,700' rel='stylesheet' type='text/css'> | |
| <link href='https://fonts.googleapis.com/css?family=Inconsolata:400,700&subset=latin,latin-ext' rel='stylesheet' | |
| type='text/css'> | |
| <style type="text/css"> | |
| body { | |
| font-family: "Open Sans", Arial, sans-serif; | |
| margin-left: 25px; | |
| margin-right: 30px; | |
| padding: 0 0 0 0; | |
| } | |
| pre { | |
| font-family: Inconsolata, "Courier New", monospace; | |
| font-size: 15px; | |
| clear: both; | |
| margin: 0 -0px 0 0; | |
| background: #000; | |
| border: 1px groove #ccc; | |
| color: #ccc; | |
| display: block; | |
| width: 100%; | |
| min-height: 600px; | |
| padding: 5px 5px 5px 5px; | |
| } | |
| .logo { | |
| float: left; | |
| margin-right: 25px;; | |
| } | |
| .scriptInfo { | |
| float: left; | |
| } | |
| h1 { | |
| font-size: 16px; | |
| margin-top: 0; | |
| line-height: 49px; | |
| margin-bottom: 0; | |
| } | |
| h1 span { | |
| font-family: Inconsolata, "Courier New", monospace; | |
| } | |
| .header { | |
| width: 100%; | |
| height: auto; | |
| display: table; | |
| margin-top: 25px; | |
| margin-bottom: 25px;; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <div class="logo"> | |
| <img src="skin/frontend/default/modern/images/logo.gif" alt="Magento's Logo" width="173" height="49"> | |
| </div> | |
| <div class="scriptInfo"> | |
| <h1>Delete Catalog Orphan Images <span>1.1</span></h1> | |
| </div> | |
| </div> | |
| <pre> | |
| <?php | |
| require 'app/Mage.php'; | |
| /** | |
| * NOTE: Set this to false if you want to actually delete files. | |
| */ | |
| $dryRun = true; | |
| $job = new OrphanImagesCleaner(); | |
| $job->run($dryRun); | |
| /** | |
| * Class OrphanImagesCleaner | |
| * | |
| * @author Jeroen Boersma <jeroen@srcode.nl> | |
| * @author Adriano Cataluddi <acataluddi@gmail.com> | |
| */ | |
| class OrphanImagesCleaner | |
| { | |
| /** | |
| * @var bool | |
| */ | |
| protected $dryRun = true; | |
| /** | |
| * Executes the tools | |
| * @param bool $dryRun | |
| */ | |
| public function run($dryRun = true) | |
| { | |
| $this->dryRun = $dryRun; | |
| $duplicateCount = 0; | |
| $duplicateTotalSize = 0; | |
| $orphansCount = 0; | |
| $orphansTotalSize = 0; | |
| if (!Mage::isInstalled()) { | |
| $this->wl('Application is not installed yet, please complete install wizard first.'); | |
| exit; | |
| } | |
| set_time_limit(0); | |
| session_write_close(); | |
| umask(0); | |
| Mage::app('admin')->setUseSessionInUrl(false); | |
| $mediaPath = $this->getRootPath() . '/media/catalog/product'; | |
| if ($this->dryRun) | |
| $this->wl('DRY RUN mode: the script will NOT modify any file or record.'); | |
| $this->wl(); | |
| $this->wl(' Magento release: ' . Mage::getVersion()); | |
| $this->wl(' Media path: ' . $mediaPath); | |
| $this->wl(); | |
| $this->wl(); | |
| $this->wl('[Phase 1/2] Looking for duplicate products images...'); | |
| $this->wl(str_repeat('-', 80)); | |
| $connection = Mage::getSingleton('core/resource') | |
| ->getConnection('core_write'); | |
| $sql = 'select distinct ' | |
| . 'cp.entity_id, ' | |
| . 'cpg.value_id, ' | |
| . 'cpv.value as default_value, ' | |
| . 'cpg.value ' | |
| . 'from catalog_product_entity as cp ' | |
| . 'join catalog_product_entity_varchar as cpv on cp.entity_id = cpv.entity_id ' | |
| . 'join catalog_product_entity_media_gallery as cpg on cp.entity_id = cpg.entity_id ' | |
| . 'WHERE ' | |
| . 'cpv.attribute_id in(select attribute_id from eav_attribute where frontend_input = \'media_image\') ' | |
| . 'and ' | |
| . 'cpv.value != cpg.value;'; | |
| $results = $connection->fetchAll($sql); | |
| $this->wl(sprintf('Found %s items to process.', sizeof($results)), true); | |
| $lastEntityId = null; | |
| $origSums = array(); | |
| foreach ($results as $row) { | |
| if ($row['entity_id'] != $lastEntityId) { | |
| $lastEntityId = $row['entity_id']; | |
| $origSums = array(); | |
| } | |
| $origFile = $mediaPath . $row['default_value']; | |
| if (!file_exists($origFile)) { | |
| continue; | |
| } | |
| $file = $mediaPath . $row['value']; | |
| if (file_exists($file)) { | |
| if (!isset($origSums[$origFile])) { | |
| $origSums[$origFile] = md5_file($origFile); | |
| } | |
| $sum = md5_file($file); | |
| if (!in_array($sum, $origSums)) { | |
| $origSums[$file] = $sum; | |
| } | |
| else { | |
| $this->wl(sprintf('Deleting image "%s" (#%s)', $file, $row['entity_id']), true); | |
| $duplicateCount++; | |
| $duplicateTotalSize += filesize($file); | |
| if (!$this->dryRun) unlink($file); | |
| } | |
| } | |
| if (!file_exists($file)) { | |
| $this->wl(sprintf('Deleting record for "%s" (#%s)', $file, $row['entity_id']), true); | |
| $deleteSql = 'delete from catalog_product_entity_media_gallery where value_id = ' . $row['value_id'] . ';'; | |
| if (!$this->dryRun) $connection->query($deleteSql); | |
| } | |
| } | |
| // Find files on filesystem which aren't listed in the database | |
| $this->wl(); | |
| $this->wl('[Phase 1/2] Finding files on filesystem which aren\'t listed in the database...'); | |
| $this->wl(str_repeat('-', 80)); | |
| $files = glob($mediaPath . '/[A-z0-9]/*/*'); | |
| foreach ($files as $file) { | |
| $searchFile = str_replace($mediaPath, '', $file); | |
| // Lookup | |
| $mediaSql = "select count(*) as records from catalog_product_entity_media_gallery where value = '{$searchFile}'"; | |
| $mediaCount = $connection->fetchOne($mediaSql); | |
| if ($mediaCount < 1) { | |
| $orphansCount++; | |
| $orphansTotalSize += filesize($file); | |
| $this->wl(sprintf('Deleting image "%s"', $file), true); | |
| if (!$this->dryRun) unlink($file); | |
| } | |
| } | |
| $this->wl(); | |
| $this->wl('Done.'); | |
| $this->wl(str_repeat('-', 80)); | |
| $this->wl(sprintf(' Total duplicate images: %s (%s)', $duplicateCount, $this->formatBytes($duplicateTotalSize))); | |
| $this->wl(sprintf(' Total orphan images: %s (%s)', $orphansCount, $this->formatBytes($orphansTotalSize))); | |
| $this->wl(str_repeat('-', 80)); | |
| } | |
| /** | |
| * @param boolean $dryRun | |
| */ | |
| public function setDryRunEnabled($dryRun) | |
| { | |
| $this->dryRun = $dryRun; | |
| } | |
| /** | |
| * @return boolean | |
| */ | |
| public function isDryRunEnabled() | |
| { | |
| return $this->dryRun; | |
| } | |
| /** | |
| * Writes a line in console. | |
| * @param $line | |
| * @param bool $notifyDryRun | |
| */ | |
| protected function wl($line = null, $notifyDryRun = false) | |
| { | |
| ($notifyDryRun && $this->dryRun && ($line !== null)) ? | |
| $dryLabel = 'DRY RUN | ' : | |
| $dryLabel = ''; | |
| print $dryLabel . $line . "\n"; | |
| } | |
| /** | |
| * Returns the script root path | |
| * @return string | |
| */ | |
| protected function getRootPath() | |
| { | |
| return (dirname(__FILE__)); | |
| } | |
| /** | |
| * Format bytes | |
| * @author MrCaspan (https://github.com/MrCaspan) | |
| * @param $bytes | |
| * @return string | |
| */ | |
| protected function formatBytes($bytes) | |
| { | |
| $i = floor(log($bytes, 1024)); | |
| return round($bytes / pow(1024, $i), [0, 0, 2, 2, 3][$i]) . ['B', 'kB', 'MB', 'GB', 'TB'][$i]; | |
| } | |
| } | |
| ?> | |
| </pre> | |
| </body> | |
| </html> |
Hi,
In my case the script didn't run as I am using a prefix for the database. Could I suggest adding that as an option? I've amended the applicable section of the code in my copy as follows:
`<?php
require 'app/Mage.php';
/**
- NOTE: Set this to false if you want to actually delete files.
*/
$dryRun = true;
/**
- NOTE: Set here the prefix for your Magento tables
*/
$db_prefix = 'bgb_';
$job = new OrphanImagesCleaner();
$job->run($dryRun, $db_prefix);
/**
-
Class OrphanImagesCleaner
-
@author Jeroen Boersma jeroen@srcode.nl
-
@author Adriano Cataluddi acataluddi@gmail.com
/
class OrphanImagesCleaner
{
/*- @var bool
*/
protected $dryRun = true;
/**
-
Executes the tools
-
@param bool $dryRun
*/
public function run($dryRun = true, $db_prefix = '')
{
$this->dryRun = $dryRun;
$duplicateCount = 0;
$duplicateTotalSize = 0;
$orphansCount = 0;
$orphansTotalSize = 0;
if (!Mage::isInstalled()) {
$this->wl('Application is not installed yet, please complete install wizard first.');
exit;
}set_time_limit(0);
session_write_close();
umask(0);Mage::app('admin')->setUseSessionInUrl(false);
$mediaPath = $this->getRootPath() . '/media/catalog/product';if ($this->dryRun)
$this->wl('DRY RUN mode: the script will NOT modify any file or record.');$this->wl();
$this->wl(' Magento release: ' . Mage::getVersion());
$this->wl(' Media path: ' . $mediaPath);
$this->wl();$this->wl();
$this->wl('[Phase 1/2] Looking for duplicate products images...');
$this->wl(str_repeat('-', 80));$connection = Mage::getSingleton('core/resource')
->getConnection('core_write');$sql = 'select distinct '
. 'cp.entity_id, '
. 'cpg.value_id, '
. 'cpv.value as default_value, '
. 'cpg.value '
. 'from ' . $db_prefix . 'catalog_product_entity as cp '
. 'join ' . $db_prefix . 'catalog_product_entity_varchar as cpv on cp.entity_id = cpv.entity_id '
. 'join ' . $db_prefix . 'catalog_product_entity_media_gallery as cpg on cp.entity_id = cpg.entity_id '
. 'WHERE '
. 'cpv.attribute_id in(select attribute_id from ' . $db_prefix . 'eav_attribute where frontend_input = 'media_image') '
. 'and '
. 'cpv.value != cpg.value;';$results = $connection->fetchAll($sql);
$this->wl(sprintf('Found %s items to process.', sizeof($results)), true);$lastEntityId = null;
$origSums = array();
foreach ($results as $row) {if ($row['entity_id'] != $lastEntityId) { $lastEntityId = $row['entity_id']; $origSums = array(); } $origFile = $mediaPath . $row['default_value']; if (!file_exists($origFile)) { continue; } $file = $mediaPath . $row['value']; if (file_exists($file)) { if (!isset($origSums[$origFile])) { $origSums[$origFile] = md5_file($origFile); } $sum = md5_file($file); if (!in_array($sum, $origSums)) { $origSums[$file] = $sum; } else { $this->wl(sprintf('Deleting image "%s" (#%s)', $file, $row['entity_id']), true); $duplicateCount++; $duplicateTotalSize += filesize($file); if (!$this->dryRun) unlink($file); } } if (!file_exists($file)) { $this->wl(sprintf('Deleting record for "%s" (#%s)', $file, $row['entity_id']), true); $deleteSql = 'delete from ' . $db_prefix . 'catalog_product_entity_media_gallery where value_id = ' . $row['value_id'] . ';'; if (!$this->dryRun) $connection->query($deleteSql); }}
// Find files on filesystem which aren't listed in the database
$this->wl();
$this->wl('[Phase 1/2] Finding files on filesystem which aren't listed in the database...');
$this->wl(str_repeat('-', 80));$files = glob($mediaPath . '/[A-z0-9]//');
foreach ($files as $file) {
$searchFile = str_replace($mediaPath, '', $file);// Lookup $mediaSql = "select count(*) as records from " . $db_prefix . "catalog_product_entity_media_gallery where value = '{$searchFile}'"; $mediaCount = $connection->fetchOne($mediaSql); if ($mediaCount < 1) { $orphansCount++; $orphansTotalSize += filesize($file); $this->wl(sprintf('Deleting image "%s"', $file), true); if (!$this->dryRun) unlink($file); }}
$this->wl();
$this->wl('Done.');
$this->wl(str_repeat('-', 80));
$this->wl(sprintf(' Total duplicate images: %s (%s)', $duplicateCount, $this->formatBytes($duplicateTotalSize)));
$this->wl(sprintf(' Total orphan images: %s (%s)', $orphansCount, $this->formatBytes($orphansTotalSize)));
$this->wl(str_repeat('-', 80));
}
/**
- @param boolean $dryRun
*/
public function setDryRunEnabled($dryRun)
{
$this->dryRun = $dryRun;
}
/**
- @return boolean
*/
public function isDryRunEnabled()
{
return $this->dryRun;
}
/**
-
Writes a line in console.
-
@param $line
-
@param bool $notifyDryRun
*/
protected function wl($line = null, $notifyDryRun = false)
{
($notifyDryRun && $this->dryRun && ($line !== null)) ?
$dryLabel = 'DRY RUN | ' :
$dryLabel = '';print $dryLabel . $line . "\n";
}
/**
- Returns the script root path
- @return string
*/
protected function getRootPath()
{
return (dirname(FILE));
}
/**
- Format bytes
- @author MrCaspan (https://github.com/MrCaspan)
- @param $bytes
- @return string
*/
protected function formatBytes($bytes)
{
$i = floor(log($bytes, 1024));
return round($bytes / pow(1024, $i), [0, 0, 2, 2, 3][$i]) . ['B', 'kB', 'MB', 'GB', 'TB'][$i];
}
}
- @var bool
?>`
@kbpcit
In case you want an alternative method which works directly from admin panel, you got this one:
https://github.com/fballiano/magento1-image-cleaner
Regards
Thanks, I have tried but it did not work.
Same issue, does not pass by
"Phase 1/2] Looking for duplicate products images..."
Will try enabling system log latter on as we are still in commercial time here...