使用PHP列出.7z、.rar和.tar档案中的文件


List files in .7z, .rar and .tar archives using PHP

我想列出档案中的文件而不提取它们

我感兴趣的档案类型:

  • .7z(7拉链)
  • .rar(WinRAR)
  • .tar(POSIX,例如GNU tar)
  • .zip(ISO标准,例如WinZip)

对于.zip文件,我已经能够实现这一点:

<?php
    $za = new ZipArchive();
    $za->open('theZip.zip');
    for ($i = 0; $i < $za->numFiles; $i++) {
        $stat = $za->statIndex($i);
        print_r(basename($stat['name']) . PHP_EOL);
    }
?>

然而,我还没有设法对.7z文件执行同样的操作。尚未测试.rar和.tar,但也需要它们。

这是以前出现过的事情(由于各种原因,比如这个和这个,以及答案中链接断开的原因)。

通常,目前的主流观点是创建一个包装器(DIY或使用库),该包装器依赖于在服务器上访问7-zip二进制文件(可执行文件),并使用exec()包装对二进制文件的调用,而不是纯PHP解决方案。

由于7zip格式支持各种压缩算法,我假设您可能想要一个读取/解压缩LZMA格式的纯PHP实现。虽然有适用于C、C++、C#和Java的LZMA SDK,并且有人已经为LZMA2制作了PHP扩展(以及LZMA的fork),尽管7-zip论坛上已经有很长一段时间的兴趣了,但似乎还没有人将其移植为全面的PECL扩展或纯PHP。

根据您的需要&动机,这给你留下了:

  • 将7-zip二进制文件添加到服务器中,并使用包装器库,无论是您自己的还是其他人的
  • 安装并使用非官方PECL扩展
  • 勇敢地将LZMA SDK移植到PHP中(并希望将其贡献回开源!)

对于其他格式,您可以查看PHP文档中的示例和用法详细信息:

  • .rar有自己的官方PECL扩展
  • 焦油可以通过Phar PECL扩展来提取(也参见SO的示例)
  • .zip有一个官方的PECL扩展
  • .gz有一个官方的PECL解释
  • 以及其他几种格式

由于所有这些都涉及PECL扩展,如果您在某种程度上受到网络主机的限制,并且需要纯PHP解决方案,那么可能更容易转向更合适的网络主机。

为了防止拉链炸弹,你可以看看这个答案所建议的压缩比(包装大小除以未包装大小,并将任何超过一定阈值的东西视为无效),尽管拉链炸弹谈到了其中一个相关问题的答案,这表明这对多层拉链炸弹可能无效。对于那些你需要查看你列出的文件是否也是档案的人,确保你没有进行任何递归提取,然后将包含档案的档案视为无效。

为了完整起见,官方PECL扩展的一些用法示例:

RAR:

<?php
// open the archive file
$archive = RarArchive::open('archive.rar');
// make sure it's valid
if ($archive === false) return;
// retrieve a list of entries in the archive
$entries = $archive->getEntries();
// make sure the entry list is valid
if ($entries === false) return;
// example output of entry count
echo "Found ".count($entries)." entries.'n";
// loop over entries
foreach ($entries as $e) {
    echo $e->getName()."'n";
}
// close the archive file
$archive->close();
?>

焦油:

<?php
// open the archive file
try {
    $archive = new PharData('archive.tar');
}
// make sure it's valid
catch (UnexpectedValueException $e) {
    return;
}
// make sure the entry list is valid
if ($archive->count() === 0) return;
// example output of entry count
echo "Found ".$archive->count()." entries.'n";
// loop over entries (PharData is already a list of entries in the archive)
foreach ($archive as $entry) {
    echo $entry."'n";
}
// no need to close a PharData
?>

ZIP(根据OP的问题改编):

<?php
// open the archive file
$archive = new ZipArchive;
$valid = $archive->open('archive.zip');
// make sure it's valid (if not ZipArchive::open() returns various error codes)
if ($valid !== true) return;
// make sure the entry list is valid
if ($archive->numFiles === 0) return;
// example output of entry count
echo "Found ".$archive->numFiles." entries.'n";
// loop over entries
for ($i = 0; $i < $archive->numFiles; $i++) {
    $e = $archive->statIndex($i);
    echo $e['name']."'n";
}
// close the archive file (redundant as called automatically at the end of the script)
$archive->close();
?>

GZ:

由于gz(gnuZlib)是一种压缩机制,而不是一种归档格式,因此在PHP中这是不同的。如果使用gzopen()单独打开.gz文件(而不是将其视为.tar),则从中读取的任何内容都将被透明地解压缩。由于这是最常见的.tar.gz,您可以将其视为如上所述的.tar(另请参阅另一个问题的答案)。或者,您可以使用PharData::decompress()提取tar,如另一个问题的答案所示。

Arnold的评论是解决问题的最实用方法的线索。即使您可以找到您希望支持的所有可能的归档类型的实现都可以访问PHP,PHP扩展也只支持ZIP和gzip。剩下的要么是本地PHP代码,要么是shell来调用独立的二进制文件。前者将是一个性能/资源瓶颈,而后者将取决于您的底层平台。

(顺便说一句,除非你完全信任用户可以访问你的服务器,或者你是一个相对优秀的程序员,否则你将不得不对内容进行更多的检查,而不仅仅是列出上传档案中的内容)。

一旦你收集了各种各样的实用程序,并将代码审计到合理的水平,你就应该用一个统一的API来装饰实现,以确保你的胶水代码不会变成意大利面条。

如果是我,我会从头开始,围绕独立的二进制文件为PHP的zip实现这样的接口;PHP毕竟是一种脚本语言。您将把它应用于用户上传的文件并不是不使用现有的本机代码实现的理由,事实上,安全考虑是这种方法的有力论据。

记得要注意外面的拉链炸弹。

我认为这个类可能会帮助您

链接的代码示例

// Open an archive.
$archive = new SevenZipArchive('docs.7z');
// Show number of contained files:
print $archive->count() . " file(s) in archive'n";
// Show info about the first contained file:
$entry = $archive->get(0);
print 'First file name: ' . $entry['Name'] . "'n";
// Iterate over all the contained files in archive, and dump all their info:
foreach ($archive as $entry) {
    print_r($entry);
}

更新
正如我在评论中所承诺的那样,OP要求提供一种检查上传文件是否有炸弹的方法,这里有一个描述它的链接。它是ClamAV®,是一个用于检测木马、病毒、恶意软件的开源防病毒引擎;其他恶意威胁源防病毒。

从ClamavNet网站我发现了这个信息

每当文件超过ArchiveMaxCompressionRatio时(请参阅clamd.conf手册页面),它被认为是一个逻辑炸弹,并被标记为Oversized.zip。试试增加ArchiveMaxCompressionRatio设置。

也就是说,我上传文件的经验来自于通常值得信赖的用户。拉链炸弹或任何其他威胁,如果我是你,我会首先研究它,找出拉链炸弹/任何其他威胁是如何工作的,这将帮助你通过额外的编码或解决方案来防止它。

此外,根据您的业务规模、预算和您的网络应用程序的关键程度,在您的网站上制定一种战略、政策和角色是一个好主意,它描述了您的web应用程序的使用情况。其中一部分是文件上传政策,比如允许上传哪种类型的文件,最大大小是多少,谁可以上传并接受你在提到这些内容时的免责声明等。该政策应反映为使用你的网络应用程序服务的受众的指南。

这里有几个关于拉链炸弹的链接:

  • 如何制造拉链炸弹
  • 我该如何保护自己免受拉链炸弹的伤害
  • https://en.wikipedia.org/wiki/Zip_bomb

试试这个

<?php
$x = exec("7z l ./test.zip | awk '/^[0-9]{4}-/{print}'", $l);
foreach($l as $r)
{
    $e = explode(" ", $r);
    $c = count($e)-1;
    echo $e[$c]."'n";
}
?>