Windows 中的 PHP ZipArchive 损坏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4620205/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 15:55:29  来源:igfitidea点击:

PHP ZipArchive Corrupt in Windows

phpwindowsziparchive

提问by Jesse Bunch

I am using PHP's ZipArchive class to create a zip file containing photos and then serve it up to the browser for download. Here is my code:

我正在使用 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;

}

This code works really well with all browsers but IE. In IE, the file downloads correctly, but the zip archive is empty. When trying to extract the files, Windows tells me that the zip archive is corrupt. Has anyone had this issue before?

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

EditUpdate: After suggestion from @profitphp, I changed my headers to this:

编辑更新:根据@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));

Also, here is a screenshot of the error in Windows after opening with Firefox:

此外,这是使用 Firefox 打开后 Windows 中错误的屏幕截图:

alt text

替代文字

This error occurs in both IE and Firefox on Windows. It works fine in Mac. Also, in Windows, the filesize appears to be correct:

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

alt text

替代文字

Edit #2This issue is sovled. See my answer below.

编辑 #2这个问题已解决。请看我下面的回答。

采纳答案by Jesse Bunch

Ok, after much strife, I figured out the problem. The issue comes from the following line of code:

好吧,经过一番挣扎,我找到了问题所在。问题来自以下代码行:

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

For some reason, the /press_photos/part of that path for the local (internal) file name inside the zip archive was causing Windows to think the zip file was corrupt. After modifying the line to look like what is below, Windows opened the zip files correctly. Phew.

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

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

回答by CWSpear

I had this same problem, and my solution was similar to the correct answer on this thread. When you put a file in the archive, you can't have absolute files (files starting with a slash) or else it won't open in Windows for some reason.

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

So got it working not because he (Jesse Bunch, the selected answer at the time of this writing) removed the containing folder but because he removed the starting slash.

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

I fixed the issue by changing

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

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

to

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

and then it was able to open in Windows!

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

That's probably the same reason pclzip (Plahcinski's answer) works. I bet it automatically strips off the beginning slash.

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

I wouldn't have figured this out without a particular commenton the PHP ZipArchive::addFiledocumentation page.

如果没有在 PHP文档页面上的特定评论,我就不会想到这一点ZipArchive::addFile

回答by Plahcinski

I recently had a similar issue as you described. I found ZipArchive to be unstable at best.

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

I solved my problems with this simple library

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

http://www.phpconcept.net/pclzip

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 = folder i wanted to zip $destination = zip file location

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

I spent 2 days looking to ZipArchive and then solved all problems with PCLZip in 5 minutes.

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

Hope this helps you and anyone else having this issue (as this is near the top google result on the issue).

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

回答by Luca Camillo

All of this suggestions may help you, but in my case I need to write an ob_clean(); before first header(''); because some file that I include before print some characters that broken zip file on windows.

所有这些建议都可能对您有所帮助,但就我而言,我需要编写一个 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;

回答by David Spector

The use of special characters, such as underscore, causes problems because ZipArchive requires IBM850 encoded entrynames. See comments in the online PHP manual: http://www.php.net/manual/en/function.ziparchive-addfile.php#95725.

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

回答by profitphp

I've had issues with this before. Try taking off the content type header. here is the code i came up with for it that worked in IE and FF. Notice the commented lines, was having the same issues with different combos of those being on.

我以前遇到过这个问题。尝试去掉内容类型标题。这是我为它想出的在 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)); 

回答by Shakus

In addition to what others suggested, it's important pay Attention to your file and directory namesas Windows does not necessarily like Linux file path and names. It sometimes also escapes them differently when zipping. Examples are numerous, but most importantly

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

  • *dot files (. and ..), files with only case differences (name.txt and NAME.txt),
  • absolute file paths (/tmp/file.txt)*.
  • Some other characters which are allowed in file names on Windows could cause issues when Windows Explorer is used to open files. In my case ':' character was the deal breaker but took a lot of work to find this out.
  • *dot 文件(. 和 ..),只有大小写不同的文件(name.txt 和 NAME.txt),
  • 绝对文件路径 (/tmp/file.txt)*。
  • 当使用 Windows 资源管理器打开文件时,Windows 上文件名中允许的一些其他字符可能会导致问题。在我的情况下,':' 字符是交易破坏者,但花了很多工作才发现这一点。

So before you resume to using using a lot of parameters through exec('zip ...'), I suggest follow a simple procedure:

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

  1. Locate the folder or file your website zips up.
  2. run: zip -9 -r -k zip-modified-names.zip /path/to/your/folder
  3. pay attention to what the console spits out.In my case ':' in file names were stripped out.
  4. Move the zip file to a windows machine and attempt to open it.
  1. 找到您的网站压缩的文件夹或文件。
  2. 运行: zip -9 -r -k zip-modified-names.zip /path/to/your/folder
  3. 注意控制台吐出的内容。在我的情况下,文件名中的 ':' 被删除了。
  4. 将 zip 文件移动到 Windows 机器并尝试打开它。

If this works, you may be better off removing the characters that have been stripped off by -k option from your file/directory names try zipping normally. Note some parameters such as -k have side effects.In this case -k contradicts with -q option (for sym links).

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

Also -k option may render your file names unreadable. In my case my files were named based on creation time (e.g. 10:55:39.pdf) to facilitate easily locating the required record from archives, but -k option turned it to 105539.pdf which is not easily readable by users. I hence changed the names to 10_55_39.pdf which opens on Windows without using -k option but is still readable.

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

In addition to this, using PCLZip would make your life a lot easier as you can add a whole folder at once and also modify path of files in one simple line. In my case I remove /tmp/directory/ from my zip files with the second and third parameters which avoids another windows compatibility issue ( having absolute path in zip files ):

除此之外,使用 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));
}

回答by Joshy Francis

            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

This worked for me while trying the above solutions.

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

回答by pu-c

I had the same problem. This code worked for me, but I HAD TO PUT IN THE FIRST LINE in my php-File! If i put the code in the middle of the File i didn't work. Maybe some encoding issues ?!

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

回答by Cesar Bielich

For those of you still banging your head after trying all this and it still didn't work I was able to fix my issue like this.

对于那些在尝试了所有这些之后仍然头疼的人,它仍然没有用,我能够像这样解决我的问题。

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

My issue was that my file paths were coming in as strings. Even though I tried (string)$pathit still did not work. For me it was using file_get_contentsof the file then the built in function addFromStringthat did it for me.

我的问题是我的文件路径是作为字符串输入的。即使我尝试了(string)$path它仍然没有奏效。对我来说,它是使用file_get_contents文件,然后是addFromString为我做的内置函数。