-
-
Save aleron75/07ab2a950b2e3429a820 to your computer and use it in GitHub Desktop.
<?php | |
require_once 'abstract.php'; | |
class Mage_Shell_CheckImages extends Mage_Shell_Abstract | |
{ | |
const CATALOG_PRODUCT = '/catalog/product'; | |
const CACHE = '/cache/'; | |
protected function _glob_recursive($pattern, $flags = 0) | |
{ | |
$files = glob($pattern, $flags); | |
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { | |
$files = array_merge($files, $this->_glob_recursive($dir . '/' . basename($pattern), $flags)); | |
} | |
return $files; | |
} | |
public function run() | |
{ | |
if ($this->getArg('help')) { | |
echo $this->usageHelp(); | |
return; | |
} | |
$media = Mage::getBaseDir('media'); | |
$debug = $this->getArg('debug'); | |
$dryrun = $this->getArg('dry') ? true : false ; | |
$includeCache = $this->getArg('cache'); | |
$imagesOnDb = array(); | |
$imagesOnDisk = array(); | |
$setup = new Mage_Core_Model_Resource_Setup('core_setup'); | |
/** @var Varien_Db_Adapter_Pdo_Mysql $connection */ | |
$connection = $setup->getConnection(); | |
$sql = "SELECT DISTINCT value | |
FROM ( | |
SELECT value | |
FROM `{$setup->getTable('catalog_product_entity_media_gallery')}` | |
WHERE attribute_id | |
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('media_gallery') AND entity_type_id = 4) | |
UNION | |
SELECT value | |
FROM `{$setup->getTable('catalog_product_entity_varchar')}` | |
WHERE attribute_id | |
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('image','small_image','thumbnail') AND entity_type_id = 4) | |
) AS T"; | |
$result = $connection->query($sql); | |
foreach ($result->fetchAll() as $rec) { | |
$imagesOnDb[$rec['value']] = 1; | |
} | |
$imagesOnDb = array_keys($imagesOnDb); | |
if ($debug) print_r(array_slice($imagesOnDb, 0, 100)); | |
if ($debug) echo $media . "/*\n"; | |
$skip = strlen($media . self::CATALOG_PRODUCT); | |
foreach ($this->_glob_recursive($media . self::CATALOG_PRODUCT . '/*', GLOB_MARK) as $img) { | |
if (substr($img, -1) != '/') { | |
if ($includeCache || (substr($img, $skip, 7) != self::CACHE)) { | |
$imagesOnDisk[] = substr($img, $skip); | |
} | |
} | |
} | |
if ($debug) print_r(array_slice($imagesOnDisk, 0, 100)); | |
$imagesToDelete = array_diff($imagesOnDisk, $imagesOnDb); | |
if ($debug) { | |
print_r($imagesToDelete); | |
} else { | |
foreach ($imagesToDelete as $x) { | |
if ($dryrun) { | |
echo 'rm '.$media . self::CATALOG_PRODUCT . $x.PHP_EOL; | |
} else { | |
@unlink($media . self::CATALOG_PRODUCT . $x); | |
} | |
} | |
} | |
if ($debug) { | |
echo "\r\n"; | |
} | |
} | |
public function usageHelp() | |
{ | |
return <<<USAGE | |
Usage: php -f check_images.php | |
debug debug mode | |
cache remove cache images too | |
dry dryrun | |
USAGE; | |
} | |
} | |
$shell = new Mage_Shell_CheckImages(); | |
$shell->run(); |
How do I get to this page on ftp?Can I have the path please.
I confirm the code it is working in Magento 1.9.2.3. It was great having some information like in Magento default shell php files (arguments for the file like status, clean, usage)
@Shumani: Click an [Raw] button, Copy the code from window, create a file on your computer then upload it with Filezilla.
Hi @aleron75 ;)
I slightly modified your script to avoid to delete placeholders. Moreover I added some stats to debug mode. Hope to be useful.
<?php
require_once 'abstract.php';
class Mage_Shell_CheckImages extends Mage_Shell_Abstract
{
const CATALOG_PRODUCT = '/catalog/product';
const CACHE = '/cache/';
const PLACEHOLDER = '/placeholder/';
protected function _glob_recursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this->_glob_recursive($dir . '/' . basename($pattern), $flags));
}
return $files;
}
public function run()
{
if ($this->getArg('help')) {
echo $this->usageHelp();
return;
}
$media = Mage::getBaseDir('media');
$debug = $this->getArg('debug');
$dryrun = $this->getArg('dry') ? true : false ;
$includeCache = $this->getArg('cache');
$imagesOnDb = array();
$imagesOnDisk = array();
$setup = new Mage_Core_Model_Resource_Setup('core_setup');
/** @var Varien_Db_Adapter_Pdo_Mysql $connection */
$connection = $setup->getConnection();
$sql = "SELECT DISTINCT value
FROM (
SELECT value
FROM `{$setup->getTable('catalog_product_entity_media_gallery')}`
WHERE attribute_id
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('media_gallery') AND entity_type_id = 4)
UNION
SELECT value
FROM `{$setup->getTable('catalog_product_entity_varchar')}`
WHERE attribute_id
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('image','small_image','thumbnail') AND entity_type_id = 4)
) AS T";
$result = $connection->query($sql);
foreach ($result->fetchAll() as $rec) {
$imagesOnDb[$rec['value']] = 1;
}
$imagesOnDb = array_keys($imagesOnDb);
if ($debug) print_r(array_slice($imagesOnDb, 0, 100));
if ($debug) echo $media . "/*\n";
$skip = strlen($media . self::CATALOG_PRODUCT);
foreach ($this->_glob_recursive($media . self::CATALOG_PRODUCT . '/*', GLOB_MARK) as $img) {
if (substr($img, -1) != '/') {
if ((substr($img, $skip, 13) != self::PLACEHOLDER) && ($includeCache || (substr($img, $skip, 7) != self::CACHE))) {
$imagesOnDisk[] = substr($img, $skip);
}
}
}
if ($debug) print_r(array_slice($imagesOnDisk, 0, 100));
$imagesToDelete = array_diff($imagesOnDisk, $imagesOnDb);
if ($debug) {
print_r($imagesToDelete);
echo count($imagesOnDisk)." images on Disk\n";
echo count($imagesOnDb)." images on DB\n";
echo count($imagesToDelete)." images to delete\n";
} else {
foreach ($imagesToDelete as $x) {
if ($dryrun) {
echo 'rm '.$media . self::CATALOG_PRODUCT . $x.PHP_EOL;
} else {
@unlink($media . self::CATALOG_PRODUCT . $x);
}
}
}
if ($debug) {
echo "\r\n";
}
}
public function usageHelp()
{
return <<<USAGE
Usage: php -f check_images.php
debug debug mode
cache remove cache images too
dry dryrun
USAGE;
}
}
$shell = new Mage_Shell_CheckImages();
$shell->run();
Where i must put this script and how can i run it from terminal?
@dvakerlis Judging from the abstract path, it's on the same folder. Most probably, shell folder.
Hi, I have tried to run this script by copying in a file with the extension php, than uploading it to my magento installation root and running it by www.mysite.de/image-clear.php, but nothing happened. Any ideas what I did wrong? Thanks in advance.
Working perfectly and really fast, it remove 12,5GB in just few seconds. Answering mittelgelb, you have to upload the script to the /shell folder and then execute it from the command line: something like that:
/usr/bin/php shell_delete_unused_images.php
Thanks for the script!
If you have a strange site setup where the entity_type_id
for products isn't "4", this script will return 0 results for images in the database. In turn, that will try and delete every single product image uploaded, which is not ideal.
The following modified script (taken from @manueltoniato 's changes) will check for the correct entity_type_id
and use that in the following select query:
<?php
require_once 'abstract.php';
class Mage_Shell_CheckImages extends Mage_Shell_Abstract
{
const CATALOG_PRODUCT = '/catalog/product';
const CACHE = '/cache/';
const PLACEHOLDER = '/placeholder/';
protected function _glob_recursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this->_glob_recursive($dir . '/' . basename($pattern), $flags));
}
return $files;
}
public function run()
{
if ($this->getArg('help')) {
echo $this->usageHelp();
return;
}
$media = Mage::getBaseDir('media');
$debug = $this->getArg('debug');
$dryrun = $this->getArg('dry') ? true : false ;
$includeCache = $this->getArg('cache');
$imagesOnDb = array();
$imagesOnDisk = array();
$setup = new Mage_Core_Model_Resource_Setup('core_setup');
/** @var Varien_Db_Adapter_Pdo_Mysql $connection */
$connection = $setup->getConnection();
$entityTypeIdSql = sprintf(
"SELECT `entity_type_id` FROM `%s` WHERE `entity_type_code` = :entity_type_code;",
$setup->getTable('eav/entity_type')
);
$entityTypeId = (int)$connection->fetchOne($entityTypeIdSql, array('entity_type_code' => Mage_Catalog_Model_Product::ENTITY));
if (!$entityTypeId) {
throw new RuntimeException(sprintf(
"Could not find entity type code for entity %s",
Mage_Catalog_Model_Product::ENTITY
));
}
$sql = "SELECT DISTINCT value
FROM (
SELECT value
FROM `{$setup->getTable('catalog_product_entity_media_gallery')}`
WHERE attribute_id
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('media_gallery') AND entity_type_id = :entity_type_id)
UNION
SELECT value
FROM `{$setup->getTable('catalog_product_entity_varchar')}`
WHERE attribute_id
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('image','small_image','thumbnail') AND entity_type_id = :entity_type_id)
) AS T";
$result = $connection->query($sql, array('entity_type_id' => $entityTypeId));
foreach ($result->fetchAll() as $rec) {
$imagesOnDb[$rec['value']] = 1;
}
$imagesOnDb = array_keys($imagesOnDb);
if ($debug) print_r(array_slice($imagesOnDb, 0, 100));
if ($debug) echo $media . "/*\n";
$skip = strlen($media . self::CATALOG_PRODUCT);
foreach ($this->_glob_recursive($media . self::CATALOG_PRODUCT . '/*', GLOB_MARK) as $img) {
if (substr($img, -1) != '/') {
if ((substr($img, $skip, 13) != self::PLACEHOLDER) && ($includeCache || (substr($img, $skip, 7) != self::CACHE))) {
$imagesOnDisk[] = substr($img, $skip);
}
}
}
if ($debug) print_r(array_slice($imagesOnDisk, 0, 100));
$imagesToDelete = array_diff($imagesOnDisk, $imagesOnDb);
if ($debug) {
print_r($imagesToDelete);
echo count($imagesOnDisk)." images on Disk\n";
echo count($imagesOnDb)." images on DB\n";
echo count($imagesToDelete)." images to delete\n";
} else {
foreach ($imagesToDelete as $x) {
if ($dryrun) {
echo 'rm '.$media . self::CATALOG_PRODUCT . $x.PHP_EOL;
} else {
@unlink($media . self::CATALOG_PRODUCT . $x);
}
}
}
if ($debug) {
echo "\r\n";
}
}
public function usageHelp()
{
return <<<USAGE
Usage: php -f check_images.php
debug debug mode
cache remove cache images too
dry dryrun
USAGE;
}
}
$shell = new Mage_Shell_CheckImages();
$shell->run();
Step 1:
Check directory size before execution
du -s
Copy Mr. dajve code and create a file in
vi shell/shell_delete_unused_images.php
Step 3:
Execute file
php shell/shell_delete_unused_images.php
Step 4:
Check directory size after execution
du -s
I would go for du -sh
to see the size in human readable form.
Hi, there is a way to also delete the files, since it only does not show them inside the products, the files are within the means?
thanks!
Hi @tarikuli can you please tell me how can i active the debug mode. i just want to check how many images to delete.
Thanks.
Beautiful! If I understand correctly, this script will delete the images that are no longer linked to no product ... That is, if I have only "disabled" products, in this case the images will not be deleted? It is important to keep them for me ... Also how do I run the script? Load in the foot and what do I type in the URL? Thank you
Hi @Ang90, this is a CLI script you must launch from the command line.
Pay attention this is a script for Magento 1.
Take users' comments int account because I didn't maintain the script.
Because I want to know, wether it is debug or dryrun, I added some echo:
<?php
require_once 'abstract.php';
class Mage_Shell_CheckImages extends Mage_Shell_Abstract
{
const CATALOG_PRODUCT = '/catalog/product';
const CACHE = '/cache/';
const PLACEHOLDER = '/placeholder/';
protected function _glob_recursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this->_glob_recursive($dir . '/' . basename($pattern), $flags));
}
return $files;
}
public function run()
{
if ($this->getArg('help')) {
echo $this->usageHelp();
return;
}
$media = Mage::getBaseDir('media');
$debug = $this->getArg('debug');
$dryrun = $this->getArg('dry') ? true : false ;
if($dryrun) {
echo "Running in dry mode.\n";
}
if($debug) {
echo "Running in debug mode.\n";
}
$includeCache = $this->getArg('cache');
$imagesOnDb = array();
$imagesOnDisk = array();
$setup = new Mage_Core_Model_Resource_Setup('core_setup');
/** @var Varien_Db_Adapter_Pdo_Mysql $connection */
$connection = $setup->getConnection();
$entityTypeIdSql = sprintf(
"SELECT `entity_type_id` FROM `%s` WHERE `entity_type_code` = :entity_type_code;",
$setup->getTable('eav/entity_type')
);
$entityTypeId = (int)$connection->fetchOne($entityTypeIdSql, array('entity_type_code' => Mage_Catalog_Model_Product::ENTITY));
if (!$entityTypeId) {
throw new RuntimeException(sprintf(
"Could not find entity type code for entity %s",
Mage_Catalog_Model_Product::ENTITY
));
}
$sql = "SELECT DISTINCT value
FROM (
SELECT value
FROM `{$setup->getTable('catalog_product_entity_media_gallery')}`
WHERE attribute_id
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('media_gallery') AND entity_type_id = :entity_type_id)
UNION
SELECT value
FROM `{$setup->getTable('catalog_product_entity_varchar')}`
WHERE attribute_id
IN (SELECT attribute_id FROM `{$setup->getTable('eav_attribute')}` WHERE `attribute_code` in ('image','small_image','thumbnail') AND entity_type_id = :entity_type_id)
) AS T";
$result = $connection->query($sql, array('entity_type_id' => $entityTypeId));
foreach ($result->fetchAll() as $rec) {
$imagesOnDb[$rec['value']] = 1;
}
$imagesOnDb = array_keys($imagesOnDb);
if ($debug) print_r(array_slice($imagesOnDb, 0, 100));
if ($debug) echo $media . "/*\n";
$skip = strlen($media . self::CATALOG_PRODUCT);
foreach ($this->_glob_recursive($media . self::CATALOG_PRODUCT . '/*', GLOB_MARK) as $img) {
if (substr($img, -1) != '/') {
if ((substr($img, $skip, 13) != self::PLACEHOLDER) && ($includeCache || (substr($img, $skip, 7) != self::CACHE))) {
$imagesOnDisk[] = substr($img, $skip);
}
}
}
if ($debug) print_r(array_slice($imagesOnDisk, 0, 100));
$imagesToDelete = array_diff($imagesOnDisk, $imagesOnDb);
if ($debug) {
print_r($imagesToDelete);
echo count($imagesOnDisk)." images on Disk\n";
echo count($imagesOnDb)." images on DB\n";
echo count($imagesToDelete)." images to delete\n";
} else {
foreach ($imagesToDelete as $x) {
if ($dryrun) {
echo 'rm '.$media . self::CATALOG_PRODUCT . $x.PHP_EOL;
} else {
@unlink($media . self::CATALOG_PRODUCT . $x);
}
}
}
if ($debug) {
echo "\r\n";
}
}
public function usageHelp()
{
return <<<USAGE
Usage: php -f check_images.php
debug debug mode
cache remove cache images too
dry dryrun
USAGE;
}
}
$shell = new Mage_Shell_CheckImages();
$shell->run();
Since my last post in 2016 I discovered the following extension for Magento 1/OpenMage: https://github.com/fballiano/openmage-image-cleaner.
This is by far the most advanced functional extension which has a lot of useful features. For a while I used ImaClean, but I removed it in favor of the extension mentioned above. It is installed in production and offers a complete visual control over what is deleted. It is not limited only to product pictures, but can also solve other places where could be orphaned images.
Hi Aleron75,
Has this been tested in Magento 1.9.2.1? If so, I'd love to try it out myself!