清理Prestashop 1.7数据库未记录的孤立图片脚本优化求助
Prestashop 1.7 冗余图片清理脚本优化方案
我运营着一个使用多年的Prestashop 1.7店铺,需要清理冗余图片。之前用脚本删除了数据库中无关联商品的图片,但仍有大量数据库未记录的图片文件残留——比如店铺当前设5种图片尺寸,新品对应6个文件,部分旧商品曾有18个文件,旧商品删除后,它的各类尺寸图(像2026-small-cart.jpg这类)还留在服务器上。
我自己写了一个脚本,遍历图片文件夹,校验图片文件名里的id_image是否存在于数据库,不存在就删除。但这个脚本只在少量嵌套目录下有效,切换到深层目录时直接崩溃,尝试用缓存减少数据库查询也没解决问题。以下是我的原代码:
$shop_root = $_SERVER['DOCUMENT_ROOT'].'/'; include('./config/config.inc.php'); include('./init.php'); $image_folder = 'img/p/'; $image_folder = 'img/p/2/0/3/2/'; // TEST, existing product $image_folder = 'img/p/2/0/2/6/'; // TEST, product deleted from DB but files in folder //$image_folder = 'img/p/2/0/2/'; // test, not working... $scan_dir = $shop_root.$image_folder; // will check only images... global $imgExt; $imgExt = array("jpg","png","gif","jpeg"); // to avoid multiple queries for the same image id... global $lastID; global $delMode; echo "<h1>Examined folder: $image_folder</h1>\r\n"; function checkFile($scan_dir,$name) { global $lastID; global $delMode; $path = $scan_dir.$name; $ext = substr($name,strripos($name,".")+1); // if is an image and file name starts with a number if (in_array($ext,$imgExt) && (int)$name>0){ // avoid extra queries... if ($lastID == (int)$name) { $inDb = $lastID; } else { $inDb = (int)Db::getInstance()->getValue('SELECT id_product FROM '._DB_PREFIX_.'image WHERE id_image ='.((int) $name)); $lastID = (int)$name; $delMode = $inDb; } // if haven't found an id_product in the DB for that id_image if ($delMode<1){ echo "- $path has no related product in the DB I'll DELETE IT<br>\r\n"; //unlink($path); } } } function checkDir($scan_dir,$name2) { echo "<h3>Elements found in the folder <i>$scan_dir$name2</i>:</h3>\r\n"; $files = array_values(array_diff(scandir($scan_dir.$name2.'/'), array('..', '.'))); foreach ($files as $key => $name) { $path = $scan_dir.$name; if (is_dir($path)) { // new loop in the subfolder checkDir($scan_dir,$name); } else { // is a file, I'll check if must be deleted checkFile($scan_dir,$name); } } } checkDir($scan_dir,'');
核心优化点
1. 替换递归遍历为迭代遍历,解决深层目录崩溃问题
递归遍历深层目录会触发PHP栈溢出错误,改用迭代队列存储待遍历目录,彻底避免栈溢出。
2. 批量预加载有效id_image,彻底减少数据库查询
一次性从数据库取出所有存在的id_image存入数组,后续直接通过数组判断,避免重复查询,效率提升数十倍。
3. 修正id_image提取逻辑
原代码(int)$name无法正确提取2026-small-cart.jpg这类文件名的ID,改用正则匹配文件名开头的数字部分,确保ID提取准确。
4. 移除全局变量,改用参数传递
全局变量易导致逻辑混乱,将共享数据(有效ID数组、图片扩展名)直接在主逻辑中处理,避免全局依赖。
5. 增加错误处理
添加目录可读性检查、文件写入权限判断、删除结果反馈,避免脚本中途报错终止。
优化后的代码
<?php $shop_root = $_SERVER['DOCUMENT_ROOT'] . '/'; include('./config/config.inc.php'); include('./init.php'); // 目标图片目录,可根据需要修改 $image_folder = 'img/p/'; // 测试用目录,注释掉上面一行启用 //$image_folder = 'img/p/2/0/3/2/'; // 存在对应商品的目录 //$image_folder = 'img/p/2/0/2/6/'; // 商品已删除但文件残留的目录 //$image_folder = 'img/p/2/0/2/'; // 深层目录测试 $scan_dir = rtrim($shop_root . $image_folder, '/') . '/'; $imgExt = ['jpg', 'png', 'gif', 'jpeg']; // 预加载所有存在的id_image到数组,避免重复查询 $validImageIds = []; $result = Db::getInstance()->executeS('SELECT id_image FROM ' . _DB_PREFIX_ . 'image'); foreach ($result as $row) { $validImageIds[(int)$row['id_image']] = true; } echo "<h1>Examined folder: $image_folder</h1>\r\n"; // 迭代遍历目录,避免递归栈溢出 $dirsToScan = [$scan_dir]; while (!empty($dirsToScan)) { $currentDir = array_pop($dirsToScan); echo "<h3>Elements found in folder <i>$currentDir</i>:</h3>\r\n"; // 跳过无法读取的目录 if (!is_dir($currentDir) || !is_readable($currentDir)) { echo "- Skipped: Directory not readable<br>\r\n"; continue; } $files = array_diff(scandir($currentDir), ['..', '.']); foreach ($files as $name) { $path = $currentDir . $name; if (is_dir($path)) { // 将子目录加入待遍历队列 array_push($dirsToScan, rtrim($path, '/') . '/'); } else { // 处理图片文件 $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if (!in_array($ext, $imgExt)) { continue; } // 提取文件名开头的id_image if (preg_match('/^(\d+)-?/', $name, $matches)) { $imageId = (int)$matches[1]; if (!isset($validImageIds[$imageId])) { echo "- $path has no related product in DB, will DELETE<br>\r\n"; // 确认无误后取消注释执行删除 // if (is_writable($path)) { // if (unlink($path)) { // echo "- Deleted successfully<br>\r\n"; // } else { // echo "- Failed to delete<br>\r\n"; // } // } else { // echo "- No permission to delete<br>\r\n"; // } } } } } } ?>
内容的提问来源于stack exchange,提问作者Nadia
相关产品推荐
相关产品推荐

