PHP中需组合scandir()、filesize()和usort()实现按大小排序,过滤.和..及目录,用绝对路径调用filesize(),重命名时加序号前缀并检查目标存在性,跨文件系统需改用copy()+unlink()。

PHP 中用 scandir() 获取文件并按大小排序
PHP 没有内置“按大小排序后批量重命名”的函数,得自己组合:先读取文件列表,再用 filesize() 获取大小,最后用 usort() 排序。注意 scandir() 默认按字母序返回,不反映文件大小顺序。
-
scandir()返回的数组包含.和..,必须过滤掉,否则filesize()会报Warning: filesize(): stat failed - 排序时别直接对字符串路径调用
filesize()—— 要传入完整绝对路径,相对路径在子目录下容易出错 - 建议用
is_file()再过滤一遍,排除目录干扰
foreach (scandir($dir) as $file) {
$path = $dir . '/' . $file;
if ($file === '.' || $file === '..' || !is_file($path)) continue;
$files[] = ['name' => $file, 'size' => filesize($path)];
}
usort($files, function($a, $b) { return $a['size'] <=> $b['size']; });
按大小升序/降序重命名文件(带序号前缀)
排序后替换文件名,常见需求是加统一前缀 + 自增序号,比如 001_原文件.jpg。关键点在于:新文件名不能和已有文件冲突,且要保留原扩展名。
- 用
pathinfo($file, PATHINFO_EXTENSION)安全提取扩展名,比substr(strrchr())更可靠 - 序号格式用
sprintf('%03d', $i)控制位数,避免 1、10、2 乱序 - 执行
rename()前务必检查目标路径是否已存在,否则会静默覆盖(PHP 不抛异常,只返回false)
$i = 1;
foreach ($files as $item) {
$old = $dir . '/' . $item['name'];
$ext = pathinfo($item['name'], PATHINFO_EXTENSION);
$new_name = sprintf('%03d_%s', $i++, $item['name']);
$new = $dir . '/' . $new_name;
if (!file_exists($new)) {
rename($old, $new);
} else {
echo "跳过:$new 已存在\n";
}
}
遇到权限或跨文件系统时 rename() 失败怎么办
rename() 在 Linux 下跨分区(不同 mount point)、Windows 下跨盘符时会失败,错误提示通常是 Operation not permitted 或直接返回 false。这不是代码逻辑问题,而是系统限制。
- 先用
realpath($old)和realpath($new)检查是否在同一文件系统(stat($path)['dev']相同才安全) - 不跨系统时失败,大概率是目录写权限不足 —— 确保 PHP 进程对
$dir有w权限,而非仅文件有w - 真要跨分区,改用
copy()+unlink()组合,但注意copy()不保留原文件权限/时间戳
大目录下性能明显变慢?别用 scandir() + 全量 filesize()
如果目录含几千个文件,每个都调 filesize() 会产生大量系统调用,I/O 成瓶颈。Linux 下可用 shell_exec('ls -S') 借系统命令提速,但需确保环境可控(禁用用户输入拼接,避免命令注入)。
立即学习“PHP免费学习笔记(深入)”;
-
ls -S按大小降序,ls -Sr升序;加-p可过滤掉目录(*/结尾),再用array_filter()清理空行 - Windows 用
dir /O-S,但输出格式不一致,解析更麻烦,不推荐生产环境混用 - 真正海量文件(>10w),应考虑分块处理或改用
glob()配合stream_wrapper_register()异步读取元数据
./.. 过滤和跨分区判断 —— 这两个点一出错,要么报满屏 warning,要么静默丢文件。











