PHP ZipArchive在Windows中损坏

时间:2011-01-06 21:42:23

标签: php windows ziparchive

我使用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中的错误的屏幕截图:

alt text

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

alt text

编辑#2 此问题已被解决。请参阅下面的答案。

14 个答案:

答案 0 :(得分:23)

我遇到了同样的问题,我的解决方案类似于此主题的正确答案。当您将文件放入存档时,您不能拥有绝对文件(以斜杠开头的文件),否则它将无法在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,我就不会想到这一点。

答案 1 :(得分:14)

我最近遇到了与你描述的类似的问题。我发现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 = zip文件位置

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

希望这可以帮助您和其他任何有此问题的人(因为这个问题接近谷歌的最高结果)。

答案 2 :(得分:8)

所有这些建议都可以帮到你,但在我的情况下,我需要写一个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;

答案 3 :(得分:6)

好吧,经过多次冲突,我发现了问题。问题来自以下代码:

$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)));

答案 4 :(得分:5)

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

答案 5 :(得分:3)

我之前遇到过这个问题。尝试取消内容类型标题。这是我在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)); 

答案 6 :(得分:2)

除了其他人的建议之外,重要的是注意你的文件和目录名,因为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计算机并尝试将其打开。
  5. 如果这样可行,最好从文件/目录名中删除已被-k选项剥离的字符,并尝试正常压缩。 请注意某些参数(如-k)会产生副作用。在这种情况下,-k与-q选项(对于sym链接)相矛盾。

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

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

    artwork/$file_name

答案 7 :(得分:1)

我遇到了同样的问题。这段代码对我有用,但我必须在我的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);

答案 8 :(得分:1)

            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

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

答案 9 :(得分:0)

我在zip文件名中使用时间戳。实际上Windows文件系统不支持像“:\ / *?<> |”

这样的特殊字符

从时间部分移除“:”后,它就像魅力一样

答案 10 :(得分:0)

以防其他人正在敲打他/她在砖墙上呆了几个小时并且像我一样受苦。我有同样的问题,没有一个解决方案有帮助,直到我意识到我在我的PHP中加载了一些库,其中一个在?>之后有一个空行。码。使用include(/path/to/lib/lib.php);调用库时,会向浏览器输出一个空行,导致该zip被Windows归类为已损坏。 (Winzip,Total Commander等没有任何问题)。因此,请确保没有导入的库,或者如果有的话,它没有空格或空行....

答案 11 :(得分:0)

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

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

答案 12 :(得分:0)

对于那些尝试了所有这些方法之后仍然still头的人,但仍然无法正常工作,我可以这样解决问题。

$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

答案 13 :(得分:-3)

更改您的代码:

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

使用:

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