You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

清理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 06:35:51