Last active
December 8, 2022 14:58
-
-
Save acataluddi/e5887529981f67e03eb3ffd53a577bef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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 <[email protected]> | |
* @author Adriano Cataluddi <[email protected]> | |
*/ | |
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> |
@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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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';
/**
*/
$dryRun = true;
/**
*/
$db_prefix = 'bgb_';
$job = new OrphanImagesCleaner();
$job->run($dryRun, $db_prefix);
/**
Class OrphanImagesCleaner
@author Jeroen Boersma [email protected]
@author Adriano Cataluddi [email protected]
/
class OrphanImagesCleaner
{
/*
*/
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) {
}
// 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);
}
$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));
}
/**
*/
public function setDryRunEnabled($dryRun)
{
$this->dryRun = $dryRun;
}
/**
*/
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";
}
/**
*/
protected function getRootPath()
{
return (dirname(FILE));
}
/**
*/
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];
}
}
?>`