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

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

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

我感兴趣的档案类型:

For .zip files,我已经实现了:

<?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,但也需要它们。

Arnuld 的评论是解决问题的最实用方法的线索。即使您可以找到 PHP 支持的所有可能的存档类型的实现,PHP 扩展也仅原生支持 ZIP 和 gzip。其余部分将是本机 PHP 代码或 shell 以调用独立二进制文件。前者会有点 performance/resource 瓶颈,而后者则取决于您的底层平台。

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

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

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

记得小心拉链炸弹。

我认为这 class 可能对您有所帮助

来自 link

的代码示例
// 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 要求提供一种方法来检查上传的文件是否受到炸弹攻击,这里有一个 link 来描述它。它是一个 ClamAV® 是一个开源防病毒引擎,用于检测木马、病毒、恶意软件和其他恶意威胁源防病毒。

我从 ClamavNet 站点找到了 this 信息

Whenever a file exceeds ArchiveMaxCompressionRatio (see clamd.conf man page), it’s considered a logic bomb and marked as Oversized.zip . Try increasing your ArchiveMaxCompressionRatio setting.

也就是说,我上传文件的经验通常来自受信任的用户。 Zip 炸弹或任何其他威胁,如果我是你,我会先研究它并找出 zip bombs/any 其他威胁的工作原理,这将帮助你通过额外的编码或解决方案来防止它。

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

这里有一些关于 zip 炸弹的link:

这是以前出现的问题(出于各种原因,例如 this and and the one with broken links in the answer)。

目前普遍的观点是创建一个包装器(DIY 或使用 a library),它依赖于在服务器上可访问的 7-zip 二进制文件(可执行文件)并将调用包装到使用 exec() 而不是纯 PHP 解决方案的二进制文件。

7zip format supports a variety of compression algorithms, I'm assuming that you probably want a pure PHP implementation of reading/decompressing the LZMA format. While there are LZMA SDKs available for C, C++, C# and Java and someone has made a PHP Extension for LZMA2 (and a fork for LZMA) as yet even though there has even been interest on the 7-zip forums 以来,似乎还没有人将其移植为全面的 PECL 扩展或纯 PHP。

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

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

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

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

要尝试防止 zip 炸弹,您可以查看 (packed size divided by unpacked size and treat anything over a certain threshold as invalid), although the zip bomb talked about the answer to one of the linked questions 建议的压缩率,这表明这对多层 zip 炸弹无效。对于那些您需要查看您列出的文件是否也是档案的人,确保您没有进行任何类型的递归提取,然后将包含档案的档案视为无效。

为了完整起见,官方 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();
?>

TAR:

<?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(改编自来自 here 的 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 (gnu Zlib) 是一种压缩机制而不是存档格式,因此在 PHP 中有所不同。如果您使用 gzopen(), any reads from it are transparently decompressed. Since this is most commonly .tar.gz, you can treat it like a .tar as above (also see this answer on another question). Or you can extract the tar with PharData::decompress() as in this answer on another question.

单独打开一个 .gz 文件(而不是像 .tar 一样对待它)

试试这个

<?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";
}
?>