PHP的ZipArchive检查Zip文件是否损坏/不完整

10
我的用户通过FTP上传zip文件,然后一个php文件将它们添加到RSS文件中。
我正在尝试找到一种方法来检查每个ZIP文件,以验证文件是否损坏或上传是否未完成。有没有办法实现这个功能?
3个回答

18
< p >从open得到的结果也可以是true应该首先进行评估。如果没有检查ZipArchive:ER_NOZIP,它相当于(int)1,将始终匹配。< / p >
$zip = new ZipArchive();
$res = $zip->open('test.zip', ZipArchive::CHECKCONS);
if ($res !== TRUE) {
    switch($res) {
        case ZipArchive::ER_NOZIP:
            die('not a zip archive');
        case ZipArchive::ER_INCONS :
            die('consistency check failed');
        case ZipArchive::ER_CRC :
            die('checksum failed');
        default:
            die('error ' . $res);
    }
}

这个很好用。虽然它也会解压缩Excel和类似的文件,但大多数情况下是不需要的。 - Firze
我遇到了这样的错误:未定义类常量'CHECKCONS'。 - Er.KT
@h0tw1r3:自PHP 5.3版本起,该扩展已内置。在此之前,Windows用户需要在php.ini中启用php_zip.dll以使用这些函数。 - Er.KT
我正在使用php7,wamp和Windows 10。 - Er.KT

2
如何检测CRC不匹配的损坏文件:
ZipArchive似乎无法检测损坏的文件。ZipArchive :: CHECKCONS没有帮助,只有当它根本不是ZIP文件时才有用。在我的测试中,它可以愉快地解压缩损坏的文件,并且未通知下载数据的客户端。
创建一个用于测试的损坏档案很简单-压缩一些文件并使用十六进制编辑器更改结果ZIP文件中的一个字节。现在,您可以使用ZIP应用程序测试该文件,以了解存档内部的哪个文件已损坏。
对于较小的文件,您可以在服务器上简单地验证CRC:
<?php
$maxsize = 1024*1024;
$z = new ZipArchive;
$r = $z->open("foo.zip", ZipArchive::CHECKCONS);
if($r !== TRUE)
  die('ZIP error when trying to open "foo.zip": '.$r);

$stat = $z->statName("mybrokenfile.txt");
if($stat['size'] > $maxsize)
  die('File too large, decompression denied');
$s = $z->getStream($file);
$data = stream_get_contents($s, $maxsize);
fclose($s);
if($stat['crc'] != crc32($data))
  die('File is corrupt!');
//echo 'File is valid';

//you may send the file to the client now if you didn't output anything before
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="mybrokenfile.txt"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $stat['size']);
ob_clean();
echo $data;
$z->close();
?>

如果由于文件过大而需要在传输到客户端时进行流式解压缩,而不是在服务器上完全解压缩文件,则文件传输已经开始,稍后打印错误消息将无效。也许最好的方法是在关闭文件传输之前中断连接。客户端应该能够检测到这个下载损坏了。 在服务器端需要一个函数,可以逐步计算流数据的CRC32值。

按设计工作。CHECKCONS 不验证存档内部的文件,仅验证存档本身的一致性。 - h0tw1r3

2
您可以使用 ZipArchive 类来实现此功能。自 PHP5.2 以来,它已成为标准的 php 发布版的一部分。按以下方式使用:
$zip = new ZipArchive();

// ZipArchive::CHECKCONS will enforce additional consistency checks
$res = $zip->open('test.zip', ZipArchive::CHECKCONS);
if(!$res) {
    throw Exception('Error opening zip');
}

switch($res) {

    case ZipArchive::ER_NOZIP :
        die('not a zip archive');
    case ZipArchive::ER_INCONS :
        die('consistency check failed');
    case ZipArchive::ER_CRC :
        die('checksum failed');
    
    // ... check for the other types of errors listed in the manual
}

如果zip文件档案不完整或以其他方式损坏,$zip->open()会返回ZipArchive::ER_NOZIP

我已经知道如何检查它是否为zip归档文件,我想检查文件是否不完整或损坏。 - Anar Choi
似乎你没有理解这个技巧:如果存档文件已损坏,$zip->open() 将返回 ER_NOZIP - hek2mgl
我刚刚测试了一下,即使使用有效的zip归档文件进行测试,它始终打印“不是zip归档文件”。 - Anar Choi
是的,然后会执行额外的一致性检查。我会将此添加到答案中。但是即使没有这个,通常的一致性检查也已默认启用,所以这个例子应该能够找到损坏的存档文件(我查看了源代码)。您目前的问题是什么?它没有检测到某些损坏的存档文件吗? - hek2mgl
如果您不加条件语句 if ($res !== true),而直接使用 switch ($res),那么它会触发第一个 case。 - tim
@tim 感谢您为这篇旧文章提供的意见!我会添加进去。另外,你也可以在PHP中使用以下语句: switch(true) { case $res === ZipArchive::ER_NOZIP: ... and so on } - hek2mgl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接