【问题标题】:PHP ZipArchive Corrupt in WindowsWindows 中的 PHP ZipArchive 损坏
【发布时间】:2011-01-06 21:42:22
【问题描述】:

我正在使用 PHP 的 ZipArchive 类创建一个包含照片的 zip 文件,然后将其提供给浏览器以供下载。这是我的代码:

/**
 * Grabs the order, packages the files, and serves them up for download.
 *
 * @param string $intEntryID 
 * @return void
 * @author Jesse Bunch
 */
public static function download_order_by_entry_id($intUniqueID) {

    $objCustomer = PhotoCustomer::get_customer_by_unique_id($intUniqueID);

    if ($objCustomer):

        if (!class_exists('ZipArchive')):
            trigger_error('ZipArchive Class does not exist', E_USER_ERROR);
        endif;

        $objZip = new ZipArchive();
        $strZipFilename = sprintf('%s/application/tmp/%s-%s.zip', $_SERVER['DOCUMENT_ROOT'], $objCustomer->getEntryID(), time());

        if ($objZip->open($strZipFilename, ZIPARCHIVE::CREATE) !== TRUE):

            trigger_error('Unable to create zip archive', E_USER_ERROR);

        endif;          

        foreach($objCustomer->arrPhotosRequested as $objPhoto):

            $filename = PhotoCart::replace_ee_file_dir_in_string($objPhoto->strHighRes);
            $objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename)));

        endforeach;

        $objZip->close();

        header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($strZipFilename)).' GMT',  TRUE, 200);
        header('Cache-Control: no-cache', TRUE);
        header('Pragma: Public', TRUE);
        header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT', TRUE);
        header('Content-Length: '.filesize($strZipFilename), TRUE);
        header('Content-disposition: attachment; filename=press_photos.zip', TRUE);

        header('Content-Type: application/octet-stream', TRUE);

        ob_start();
        readfile($strZipFilename);
        ob_end_flush();
        exit;

    else:

        trigger_error('Invalid Customer', E_USER_ERROR);

    endif;

}

此代码适用于除 IE 之外的所有浏览器。在 IE 中,文件下载正确,但 zip 存档为空。尝试提取文件时,Windows 告诉我 zip 存档已损坏。以前有人遇到过这个问题吗?

编辑更新:根据@profitphp 的建议,我将标题更改为:

header("Cache-Control: public");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
//header("Content-Description: File Transfer");
//header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=\"pressphotos.zip\"");
//header("Content-Transfer-Encoding: binary");
header("Content-length: " . filesize($strZipFilename));

另外,下面是用 Firefox 打开后在 Windows 中的错误截图:

Windows 上的 IE 和 Firefox 都会出现此错误。它在 Mac 中运行良好。此外,在 Windows 中,文件大小似乎是正确的:

编辑#2这个问题已经解决了。请参阅下面的答案。

【问题讨论】:

标签: php windows ziparchive


【解决方案1】:

我遇到了同样的问题,我的解决方案与此线程上的正确答案相似。将文件放入存档时,不能有绝对文件(以斜杠开头的文件),否则由于某种原因无法在 Windows 中打开。

所以让它工作不是因为他(Jesse Bunch,在撰写本文时选择的答案)删除了包含文件夹,而是因为他删除了起始斜杠。

我通过更改解决了这个问题

$zip->addFile($file, $file); // $file is something like /path/to/file.png

// we make file relative by removing beginning slash so it will open in Windows
$zip->addFile($file, ltrim($file, '/'));

然后它就可以在Windows中打开了!

这可能与 pclzip(Plahcinski 的回答)起作用的原因相同。我敢打赌它会自动去掉开头的斜线。

如果没有 PHP ZipArchive::addFile 文档页面上的 particular comment,我不会想到这一点。

【讨论】:

  • 这对我来说是个问题,帮了很多忙
【解决方案2】:

我最近遇到了与您描述的类似的问题。我发现 ZipArchive 充其量是不稳定的。

我用这个简单的库解决了我的问题

http://www.phpconcept.net/pclzip

include_once('libs/pclzip.lib.php');

...

function zip($source, $destination){
$zipfile = new PclZip($destination);
$v_list = $zipfile->create($source, '', $source); }

$source = 我要压缩的文件夹 $destination = 压缩文件位置

我花了 2 天时间寻找 ZipArchive,然后在 5 分钟内解决了 PCLZip 的所有问题。

希望这对您和遇到此问题的其他人有所帮助(因为这接近该问题的谷歌搜索结果)。

【讨论】:

  • 在窗口环境中。在我的网页上,不幸的是它运行得很好:它只压缩了一个文件而不是几个文件,
  • 确实在 Windows 上为我工作。就像你说的,看 ZipArchive 将近一个小时,这在 2 分钟内就奏效了。 tnx!
  • 很棒的图书馆。要在 PHP7.1 服务器上使用它,只需将方法“PclZip”更改为“__construct”并在需要整数的地方添加“(int)”。
【解决方案3】:

所有这些建议都可能对您有所帮助,但就我而言,我需要编写一个 ob_clean();在第一个标头('')之前;因为我在打印一些在 Windows 上破坏 zip 文件的字符之前包含了一些文件。

$zip=new ZipArchive();
$zip->open($filename, ZIPARCHIVE::CREATE);
$zip->addFile($file_to_attach,$real_file_name_to_attach);
$zip->close();

ob_clean();
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header('Content-Type: application/x-download');
header('Content-Disposition: attachment; filename="file.zip"');
readfile($filename);
exit;

【讨论】:

  • 这对我有用,尝试了其他一切都没有运气。
  • 这个也对我有用。还尝试了其他一切。这应该是公认的答案。注意:我只需要添加一行:ob_clean() 就可以让我的代码正常工作。花了一两个小时试图获取可以打开的 zip 文件。
【解决方案4】:

好的,经过一番努力,我找到了问题所在。问题来自以下代码行:

$objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename)));

由于某种原因,zip 存档中本地(内部)文件名路径的/press_photos/ 部分导致 Windows 认为 zip 文件已损坏。将行修改为如下所示后,Windows 正确打开了 zip 文件。唷。

$objZip->addFile($filename,sprintf('%s-%s', $objPhoto->getEntryID(), basename($filename)));

【讨论】:

  • 我在添加到存档后成功地重命名了文件。如果您不需要在存档中维护目录结构,这是一个很好的解决方案: $zip->addFile($uri, basename($uri) ); $zip->renameName(basename($uri), str_replace('_', '-', basename($uri)));
【解决方案5】:

使用特殊字符(例如下划线)会导致问题,因为 ZipArchive 需要 IBM850 编码的条目名称。参见在线 PHP 手册中的 cmets:http://www.php.net/manual/en/function.ziparchive-addfile.php#95725

【讨论】:

  • 我的 zip 文件名很长,包含空格、破折号和日期字符。事实证明,一旦我将文件名更改为简单的文件名,例如 export.zip,它就可以正常工作了。
【解决方案6】:

我以前遇到过这个问题。尝试去掉内容类型标题。这是我想出的在 IE 和 FF 中工作的代码。请注意注释行,它们的不同组合存在相同的问题。

header("Cache-Control: public");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
//header("Content-Description: File Transfer");
//header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=\"adwords-csv.zip\"");
//header("Content-Transfer-Encoding: binary");
header("Content-length: " . filesize($filename)); 

【讨论】:

  • 我改变了我的标题,没有骰子,根据 windows,zip 文件仍然“损坏”,但在我的 Mac 上很好。
  • 尝试让标题看起来与我发布的完全一样,顺序相同。在 zip 中使用某些文件名时,我也遇到了类似的问题。坚持一个非常基本的字符集。现在你提到窗户。如果您在 windows 上使用 firefox、chrome 等,会发生同样的事情吗?是浏览器问题,还是 zip 阅读器问题?
  • 感谢您抽出宝贵时间,我已更新此问题,提供更多信息。
  • 好吧,它可能不是标题的东西,看起来像是 Windows 默认 zip 阅读器的东西。你有没有试过像winrar这样的东西来解压它?我只是在我的实现中使用命令行中的 zip 而不是 zip 存档类。如果最坏的情况发生,您可能想尝试一下。
  • 你说得对,当我将“损坏”的 zip 文件从 Windows 复制到我的 Mac 时,它可以顺利打开。
【解决方案7】:

除了其他人的建议之外,请务必注意您的文件和目录名称,因为 Windows 不一定喜欢 Linux 的文件路径和名称。有时,它在压缩时也会以不同的方式逃脱它们。例子很多,但最重要的是

  • *点文件(. 和 ..),只有大小写差异的文件(name.txt 和 NAME.txt),
  • 绝对文件路径 (/tmp/file.txt)*。
  • Windows 文件名中允许的其他一些字符在使用 Windows 资源管理器打开文件时可能会导致问题。在我的例子中,':' 字符是破坏交易的因素,但需要做很多工作才能发现这一点。

所以在你继续通过 exec('zip ...') 使用大量参数之前,我建议遵循一个简单的过程:

  1. 找到您的网站压缩包的文件夹或文件。
  2. 运行:zip -9 -r -k zip-modified-names.zip /path/to/your/folder
  3. 注意控制台输出的内容。在我的例子中,文件名中的 ':' 被删除了。
  4. 将 zip 文件移动到 Windows 计算机并尝试打开它。

如果这可行,您最好从文件/目录名称中删除已被 -k 选项删除的字符,然后尝试正常压缩。 注意一些参数,例如 -k 有副作用。在这种情况下 -k 与 -q 选项(用于符号链接)相矛盾。

另外 -k 选项可能会使您的文件名不可读。在我的情况下,我的文件是根据创建时间(例如 10:55:39.pdf)命名的,以便于从档案中轻松找到所需的记录,但是 -k 选项将其变为 105539.pdf,用户不易阅读。因此,我将名称更改为 10_55_39.pdf,它在 Windows 上打开而不使用 -k 选项,但仍然可读。

除此之外,使用 PCLZip 将使您的生活变得更轻松,因为您可以一次添加整个文件夹,还可以在一行中修改文件路径。在我的情况下,我使用第二个和第三个参数从我的 zip 文件中删除 /tmp/directory/,这避免了另一个 Windows 兼容性问题(在 zip 文件中有绝对路径):

$v_list = $zip->create( $sourceFolder, PCLZIP_OPT_REMOVE_PATH, $sourceFolder . DIRECTORY_SEPARATOR);
if ($v_list == 0) {
    throw new \Exception($zip->errorInfo(true));
}

【讨论】:

    【解决方案8】:

    我遇到了同样的问题。这段代码对我有用,但我必须在我的 php 文件中放入第一行!如果我将代码放在文件的中间,我就没有工作。也许是一些编码问题?!

    // Prepare File
    $file = tempnam("tmp", "zip");
    $zip = new ZipArchive();
    $zip->open($file, ZipArchive::OVERWRITE);
    
    // Staff with content
    $zip->addFile($filepathOnServer, 'mypic.jpg');
    
    // Close and send to users
    $zip->close();
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="filename.zip"');
    readfile($file);
    unlink($file);
    

    【讨论】:

      【解决方案9】:

      我遇到这个问题已经一个小时了。在尝试了 10 种不同的解决方案后,我通过确保在输出 ZIP 文件后脚本存在来解决它:

                  readfile($zip_name);
                  unlink($zip_name);
                  **exit();**
      

      【讨论】:

        【解决方案10】:
                    ob_clean(); //very important
                    // http headers for zip downloads
                    header("Pragma: public");
                    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
                    header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
                    header("Cache-Control: public");
                    header('Content-Type: application/x-download');
                    header("Content-Disposition: attachment; filename=\"filename.zip\"");
                    header("Content-Length: ".filesize($filepath ));
        
                    @readfile($filepath );
                    unlink($filepath);  //very important
                    exit;//very important
        

        在尝试上述解决方案时,这对我有用。

        【讨论】:

          【解决方案11】:

          我在 zip 文件名中使用了时间戳。实际上 windows 文件系统不支持 " : \ / * ? | "

          这样的特殊字符

          从时间部分中删除 ":" 后,它就像一个魅力

          【讨论】:

            【解决方案12】:

            以防万一其他人将他/她的头发撞在砖墙上几个小时并像我一样受苦。我遇到了同样的问题,但没有一个解决方案有帮助,直到我意识到我在我的 PHP 中加载了一些库,其中一个库在 ?> 代码之后有一个空行。当使用include(/path/to/lib/lib.php); 调用库时,一个空行被输出到浏览器,导致 zip 被归类为被 Windows 损坏。 (Winzip、Total Commander 等都没有问题)。所以请确保没有导入的库,或者如果有的话,它没有空格或空行....

            【讨论】:

            • 哦,是的。这样就可以了。
            【解决方案13】:

            对于那些在尝试了所有这些之后仍然无法解决问题的人来说,我能够像这样解决我的问题。

            $zipname = "download.zip";
            $zip = new ZipArchive;
            $zip->open($zipname, ZipArchive::CREATE);
            foreach ($files as $file) {
                # download file
                $download_file = file_get_contents($file); <- important
            
                #add it to the zip
                $zip->addFromString(basename($file), $download_file); <- important  
            }
            $zip->close();
            
            header('Content-Type: application/zip');
            //header('Content-disposition: attachment; filename='.$zipname);
            header("Content-Disposition: attachment; filename=\"$zipname\"");
            header('Content-Length: ' . filesize($zipname));
            readfile($zipname);
            unlink($zipname);
            exit;
            

            我的问题是我的文件路径是以字符串形式出现的。即使我尝试了(string)$path,它仍然没有用。对我来说,它使用了文件的file_get_contents,然后是为我完成的内置函数addFromString

            【讨论】:

              【解决方案14】:

              更改您的代码:

               header('Content-Length: '.filesize($strZipFilename), TRUE);
              

              与:

              header('Content-Length: '.file_get_contents($strZipFilename));
              

              【讨论】:

              • 这会将文件的全部内容输出为Content-Length,而不是实际大小(这是他们想要的)。这绝对不是他们想要做的(与问题无关)。他们说得对。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-04-08
              • 2018-02-13
              • 2018-05-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多