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

如何解决PHP文件扫描器处理15000个文件时的超时问题

嘿,我来帮你搞定这个扫描超时的问题!先聊聊你当前代码里拖慢进程的几个关键点,然后咱们一步步优化~

解决PHP文件扫描器超时问题的实用方案

你的代码现在遇到的问题主要来自三个地方:

  • scandir()一次性把整个目录的文件都塞进内存,15000个文件的话内存压力会很大,进程容易卡壳
  • 每遍历一个文件就执行一次INSERT,数据库连来连去的开销累加起来,效率低到爆炸
  • PHP默认的执行时间限制(一般30秒)根本扛不住这么多文件的遍历和插入操作

下面是针对性的优化方案,从易到难:

1. 先给脚本“松绑”:调整时间和内存限制

先让脚本有足够的运行空间,在代码最顶部加上这两行:

// 取消执行时间限制(或者设个足够大的值,比如300秒=5分钟)
set_time_limit(0);
// 给脚本分配更多内存,比如256M,可根据实际情况调整
ini_set('memory_limit', '256M');

这是最基础的操作,先确保脚本不会因为时间或内存不够直接挂掉。

2. 换成迭代式遍历,别一次性加载所有文件

scandir()会把目录里的所有文件一次性读进数组,文件多的时候内存直接爆。换成RecursiveDirectoryIterator来遍历,它是迭代式的,读一个处理一个,内存占用会小很多,还能自动遍历子目录(你说“一半目录”,估计是有子目录没处理吧?):

$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('manual/'));
foreach ($iterator as $file) {
    // 只处理文件,跳过.和..以及目录
    if ($file->isFile() && !$file->isDot()) {
        $filename = $file->getFilename();
        $fullPath = $file->getPathname(); // 这里是文件的完整路径,你原代码里的$file其实是文件名,可能不符合需求?
        // 接下来处理文件名拆分逻辑
    }
}

这个迭代器会自动钻进所有子目录,不用你自己写递归逻辑,省心又高效。

3. 批量插入数据库,减少SQL请求次数

你现在每插一条数据就发一次SQL请求,15000次请求数据库肯定扛不住。改成批量插入,比如每攒100条数据就插一次,能把请求次数降到150次左右,效率直接拉满:

set_time_limit(0);
ini_set('memory_limit', '256M');

$db = mysqli_connect('localhost','test','test123','tests');
// 开启事务,批量插入的时候速度更快
$db->begin_transaction();

$batchSize = 100; // 每100条插一次,可根据服务器性能调整
$insertRows = [];

$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('manual/'));
foreach ($iterator as $file) {
    if ($file->isFile() && !$file->isDot()) {
        $filename = $file->getFilename();
        $coll = explode(".", $filename);
        // 处理没有后缀的文件,避免报错
        $fname = $coll[0] ?? '';
        $lname = $coll[1] ?? '';
        $fullPath = $file->getPathname();

        // 一定要转义数据,防止SQL注入!
        $fname = $db->real_escape_string($fname);
        $lname = $db->real_escape_string($lname);
        $fullPath = $db->real_escape_string($fullPath);

        $insertRows[] = "('$fname', '$lname', '$fullPath')";

        // 攒够数量就执行插入
        if (count($insertRows) >= $batchSize) {
            $sql = "INSERT INTO `data`(`fname`, `lname`, `directory`) VALUES " . implode(',', $insertRows);
            $db->query($sql);
            $insertRows = []; // 清空数组,继续攒下一批
        }
    }
}

// 处理最后一批不够100条的数据
if (!empty($insertRows)) {
    $sql = "INSERT INTO `data`(`fname`, `lname`, `directory`) VALUES " . implode(',', $insertRows);
    $db->query($sql);
}

$db->commit(); // 提交事务
$db->close();

开启事务也很重要,它能避免每次插入都刷新磁盘,大幅提升插入速度。

4. 进阶方案:用命令行执行或者拆分任务

如果优化后还是超时,试试这些方法:

  • 用CLI模式执行:命令行下的PHP默认没有执行时间限制,比网页模式稳定得多,直接在终端跑脚本就行
  • 拆分任务:比如先扫描manual/A到manual/F的目录,再扫manual/G到manual/L,分几次执行
  • 用预处理语句:比直接拼SQL更安全,批量执行的时候效率也更高,比如:
    $stmt = $db->prepare("INSERT INTO `data`(`fname`, `lname`, `directory`) VALUES (?, ?, ?)");
    $stmt->bind_param("sss", $fname, $lname, $fullPath);
    // 循环里只需要绑定参数然后执行
    $stmt->execute();
    

最后提个醒:防SQL注入!

你的原代码直接把变量拼进SQL,很容易被注入攻击,一定要用real_escape_string()转义,或者用上面的预处理语句,安全第一~


内容的提问来源于stack exchange,提问作者Chris Ngure

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:27:55