ZipStream在PHP中动态创建的zip文件无法在OSX中打开

时间:2011-04-06 21:31:35

标签: php macos zip

我有一个包含大量媒体文件的PHP站点,用户需要能够以.zip的形式一次下载多个文件。我正在尝试使用ZipStream通过“存储”压缩动态地为拉链提供服务,所以我实际上不必在服务器上创建一个zip,因为有些文件很大而且速度太慢了。压缩它们。

这很好用,我可以尝试的每个zip程序都可以打开生成的文件,除了OS X的默认解压缩程序Archive Utility之外没有任何错误。双击.zip文件,Archive Utility确定它看起来不是真正的zip,而是压缩成.cpgz文件。

在OS X终端或StuffIt Expander中使用unzip或ditto解压缩文件没有问题,但我需要默认程序(Archive Utility)才能为我们的用户工作。

在其他可接受的zip文件中有哪些东西(标记等)可以使Archive Utility认为文件不是有效的zip?

我读过this question,它似乎描述了一个类似的问题,但我没有设置任何通用的位域位,所以它不是第三位问题,我很确定我有效crc-32是因为当我不这样做时,WinRAR会发挥作用。

我很乐意发布一些代码或链接到“坏”zip文件,如果它会有所帮助但我只是使用ZipStream,强制它进入“大文件模式”并使用“存储”作为压缩方法。

编辑 - 我也尝试了“deflate”压缩算法并得到相同的结果,所以我不认为它是“商店”。值得指出的是,我一次从存储服务器下载文件并在它们到达时将它们发送出来,因此需要在发送任何内容之前下载所有文件的解决方案不可行(极端例如5GB +的20MB文件。用户不能等到所有5GB都要在下载开始之前转移到压缩服务器,否则他们会认为它已经坏了)

这是一个140字节的“存储”压缩测试zip文件,它表现出这种行为:http://teknocowboys.com/test.zip

7 个答案:

答案 0 :(得分:9)

问题出现在“需要提取的版本”字段中,我通过对ZipStream创建的文件和Info-zip创建的文件执行十六进制差异并查看差异,尝试解决它们。 / p>

ZipStream默认将其设置为0x0603。 Info-zip将其设置为0x000A。具有以前值的Zip文件似乎在Archive Utility中不会打开。也许它不支持该版本的功能?

强制将“需要提取的版本”强制为0x000A,这使得生成的文件也可以在Archive Utility中打开,就像在其他任何地方一样。

编辑:此问题的另一个原因是如果使用Safari(用户代理版本> = 537)下载了zip文件,并且在发送Content-Length标头时您报告的文件大小不足。

我们采用的解决方案是检测Safari> = 537服务器端,如果您正在使用它,我们确定内容长度大小与实际大小之间的差异(您如何执行此操作取决于您的具体应用程序) )并且在调用$ zipStream-> finish()之后,我们回显chr(0)以达到正确的长度。生成的文件技术上格式不正确,您在zip中放置的任何注释都不会显示,但所有zip程序都可以打开它并解压缩文件。

如果你误报了你的内容长度,IE需要相同的黑客,但不是下载一个不起作用的文件,它只是不会完成下载并抛出“下载中断”。

答案 1 :(得分:3)

使用 ob_clean(); flush();

示例:

    $file =  __UPLOAD_PATH . $projectname . '/' . $fileName;

    $zipname = "watherver.zip"
    $zip = new ZipArchive(); 
    $zip_full_path_name = __UPLOAD_PATH . $projectname . '/' . $zipname;
    $zip->open($zip_full_path_name, ZIPARCHIVE::CREATE);
    $zip->addFile($file); // Adding one file for testing
    $zip->close();

    if(file_exists($zip_full_path_name)){
        header('Content-type: application/zip');
        header('Content-Disposition: attachment; filename="'.$zipname.'"');
        ob_clean();
        flush();
        readfile($zip_full_path_name);
        unlink($zip_full_path_name);
    }

答案 2 :(得分:2)

我有这个问题,但原因不同。

在我的情况下,php生成的zip将从命令行打开,但不能通过OSX中的finder打开。

我犯了一个错误,在创建zip文件之前允许一些HTML内容进入输出缓冲区并将其作为响应发回。

<some html></....>
<?php

// Output a zip file...

命令行解压缩程序显然容忍了这一点,但Mac unarchive功能却没有。

答案 3 :(得分:1)

不知道。如果外部ZipString类不起作用,请尝试其他选项。 PHP ZipArchive扩展程序对您没有帮助,因为它不支持流式传输,只能写入文件。

但您可以尝试标准的Info-zip实用程序。它可以在PHP中调用,如下所示:

#header("Content-Type: archive/zip");
passthru("zip -0 -q -r - *.*");

这会导致未压缩的zip文件直接发送回客户端。

如果这没有帮助,那么MacOS zip前端可能不喜欢未压缩的东西。然后删除-0标志。

答案 4 :(得分:1)

我在Windows和Linux上使用的InfoZip命令行工具使用版本20来获取zip的“需要提取的版本”字段。这在PHP上也是必需的,因为默认压缩是Deflate算法。因此,“提取所需的版本”字段应该是0x0014。如果将引用的ZipStream类中的“(6 <&lt; 8)+3”代码更改为“20”,则应该跨平台获得有效的Zip文件。

作者基本上告诉你zip文件是使用HPFS文件系统在OS / 2中创建的,而Zip版本需要早于InfoZip 1.0。没有多少实现知道该怎么做了;)

答案 5 :(得分:1)

对于那些在Symfony中使用ZipStream的人来说,这是您的解决方案:https://stackoverflow.com/a/44706446/136151

use Symfony\Component\HttpFoundation\StreamedResponse;
use Aws\S3\S3Client;    
use ZipStream;

//...

/**
 * @Route("/zipstream", name="zipstream")
 */
public function zipStreamAction()
{
    //test file on s3
    $s3keys = array(
      "ziptestfolder/file1.txt"
    );

    $s3Client = $this->get('app.amazon.s3'); //s3client service
    $s3Client->registerStreamWrapper(); //required

    $response = new StreamedResponse(function() use($s3keys, $s3Client) 
    {

        // Define suitable options for ZipStream Archive.
        $opt = array(
                'comment' => 'test zip file.',
                'content_type' => 'application/octet-stream'
              );
        //initialise zipstream with output zip filename and options.
        $zip = new ZipStream\ZipStream('test.zip', $opt);

        //loop keys useful for multiple files
        foreach ($s3keys as $key) {
            // Get the file name in S3 key so we can save it to the zip 
            //file using the same name.
            $fileName = basename($key);

            //concatenate s3path.
            $bucket = 'bucketname';
            $s3path = "s3://" . $bucket . "/" . $key;        

            //addFileFromStream
            if ($streamRead = fopen($s3path, 'r')) {
              $zip->addFileFromStream($fileName, $streamRead);        
            } else {
              die('Could not open stream for reading');
            }
        }

        $zip->finish();

    });

    return $response;
}

如果你的控制器动作响应不是StreamedResponse,你可能会在我发现的时候得到一个包含html的损坏的zip。

答案 6 :(得分:0)

这是一个古老的问题,但是我保留了它对我有用的东西,以防万一它可以帮助别人。 设置选项时,需要将“零标头”设置为true并将zip 64设置为false(不过,这会将存档限制为4 Gb):

<script>
$(function() {
    $('.single-date-time').daterangepicker({
        singleDatePicker: true,
        timePicker: true,
        applyClass: 'bg-slate-600',
        cancelClass: 'btn-light',
        locale: {
            format: 'MM/DD/YYYY h:mm a'
        }
    });
});
</script>
</head>
<body>
    <input type="text" class="single-date-time" name="test" />
</body>

Forer所述的所有其他内容。 在https://github.com/maennchen/ZipStream-PHP/issues/71

上找到了解决方案