如何用PHP的RecursiveIteratorIterator按原顺序遍历tar包内容?
如何在PHP中按原顺序遍历Tar包内容?
Tar包内的文件默认按加入顺序存储,通过tar tvf命令可以看到原始顺序:
# tar tvf file.tgz -rw-r--r-- 0/0 962 2023-01-17 13:40:17 6fe3b8b5-a4dc-4976-bea9-227434c11cda -rw-r--r-- 0/0 962 2023-01-17 13:40:17 febe009e-8ce0-4027-abda-e27f5f949dde -rw-r--r-- 0/0 962 2023-01-17 13:40:17 4fea07a6-a90c-4cb4-9969-4cac08422ccf -rw-r--r-- 0/0 962 2023-01-17 13:40:17 01610297-c577-4e6c-9ce9-7825a40e6d0c -rw-r--r-- 0/0 962 2023-01-17 13:40:26 01b498ca-3d87-426b-b01d-d4e75921cb00 -rw-r--r-- 0/0 962 2023-01-17 13:40:26 45821111-f69d-4331-87d8-f3afd9918c91
但使用PHP 8.1的RecursiveIteratorIterator遍历PharData对象时,文件会被自动按字母序排序:
$p = new PharData($path); foreach (new RecursiveIteratorIterator($p) as $file) { echo $file . "\n"; }
输出结果为:
phar:///path/to/file/01610297-c577-4e6c-9ce9-7825a40e6d0c phar:///path/to/file/01b498ca-3d87-426b-b01d-d4e75921cb00 phar:///path/to/file/45821111-f69d-4331-87d8-f3afd9918c91 phar:///path/to/file/4fea07a6-a90c-4cb4-9969-4cac08422ccf phar:///path/to/file/6fe3b8b5-a4dc-4976-bea9-227434c11cda phar:///path/to/file/febe009e-8ce0-4027-abda-e27f5f949dde
解决方案
方法1:调用系统tar命令解析原始顺序
如果运行环境支持tar命令,可以直接执行命令获取原始顺序的文件列表,再逐个处理:
$path = 'file.tgz'; // 执行tar命令提取文件名(按原始顺序) $output = shell_exec("tar tvf $path | awk '{print $6}'"); $files = array_filter(explode("\n", trim($output))); foreach ($files as $file) { $p = new PharData($path); $content = $p[$file]->getContent(); echo "处理文件:$file\n"; }
这种方法简单直接,能准确获取tar包的原始文件顺序,但依赖系统环境中的tar命令,跨平台性稍差。
方法2:使用第三方库Archive_Tar
PEAR的Archive_Tar库支持按原始顺序读取tar包内容,无需依赖系统命令:
首先安装库:
pear install Archive_Tar
然后编写代码:
require_once 'Archive/Tar.php'; $tar = new Archive_Tar('file.tgz'); // 获取所有文件条目(按原始顺序) $fileList = $tar->listContent(); foreach ($fileList as $fileInfo) { $filename = $fileInfo['filename']; // 提取文件内容 $content = $tar->extractInString($filename); echo "处理文件:$filename\n"; }
这个方法跨平台性更好,不需要依赖系统tar命令,但需要额外安装第三方库。
方法3:手动解析Tar格式(原生PHP)
如果不想依赖外部工具或库,可以手动解析tar文件格式,按字节顺序读取每个文件头,获取原始顺序:
$handle = fopen('file.tgz', 'rb'); $gz = gzopen($handle, 'rb'); // tar文件每个条目头固定为512字节 while (!gzeof($gz)) { $header = gzread($gz, 512); if (trim($header) === '') break; // 读取到结束块,终止循环 // 解析文件名(tar头前100字节) $filename = trim(substr($header, 0, 100)); if (empty($filename)) continue; // 解析文件大小(头中124-135字节,八进制格式) $size = octdec(trim(substr($header, 124, 11))); echo "找到文件:$filename\n"; // 跳过文件内容(tar内容按512字节对齐) $skipBytes = $size % 512 === 0 ? $size : $size + (512 - ($size % 512)); gzseek($gz, gztell($gz) + $skipBytes); } gzclose($gz); fclose($handle);
这种方法完全不依赖外部资源,但需要熟悉tar文件的格式规范,适合对环境有严格限制的场景。
内容的提问来源于stack exchange,提问作者g4b0
相关产品推荐
相关产品推荐

