通过ZipStream在PHP中动态创建zip文件;不能在OSX中打开
我有一个包含大量媒体文件的PHP站点,用户需要能够以.zip格式一次下载多个文件。我正在尝试使用“存储”压缩来提供即时拉链服务,这样我就不必在服务器上创建zip,因为有些文件很大,而且压缩速度非常慢 这非常有效,我尝试过的每个zip程序都可以打开生成的文件,除了OSX的默认解压程序Archive Utility之外,没有任何错误。双击.zip文件,存档实用程序确定它看起来不是真正的zip文件,而是压缩成.cpgz文件 在OSX终端或StuffIt Expander中使用解压或同上可以毫无问题地解压文件,但为了用户的利益,我需要默认程序(归档实用程序) 在其他可接受的zip文件中,什么样的东西(标志等)会使存档实用程序误认为文件不是有效的zip 我读过,它似乎描述了一个类似的问题,但我没有设置任何通用位域位,所以这不是第三位问题,我非常确定我有有效的crc-32,因为当我没有设置时,WinRAR会抛出一个错误 如果有帮助的话,我很乐意发布一些代码或指向“坏”zip文件的链接,但我基本上只是使用ZipStream,强制它进入“大文件模式”,并使用“存储”作为压缩方法 编辑-我也尝试过“deflate”压缩算法,得到了相同的结果,所以我认为这不是“store”。还值得指出的是,我正在从存储服务器上一次下载一个文件,并在文件到达时将其发送出去,因此要求在发送任何文件之前下载所有文件的解决方案将不可行(最极端的例子是20MB的5GB+文件。用户不能等到所有5GB传输到压缩服务器后再开始下载,否则他们会认为它坏了)通过ZipStream在PHP中动态创建zip文件;不能在OSX中打开,php,macos,zip,Php,Macos,Zip,我有一个包含大量媒体文件的PHP站点,用户需要能够以.zip格式一次下载多个文件。我正在尝试使用“存储”压缩来提供即时拉链服务,这样我就不必在服务器上创建zip,因为有些文件很大,而且压缩速度非常慢 这非常有效,我尝试过的每个zip程序都可以打开生成的文件,除了OSX的默认解压程序Archive Utility之外,没有任何错误。双击.zip文件,存档实用程序确定它看起来不是真正的zip文件,而是压缩成.cpgz文件 在OSX终端或StuffIt Expander中使用解压或同上可以毫无问题地解
这是一个140字节的“存储”压缩测试zip文件,它显示了以下行为:不知道。如果外部ZipString类不起作用,请尝试另一个选项。PHP扩展插件对您没有帮助,因为它不支持流式传输,但只会写入文件 但您可以尝试使用标准Info zip实用程序。它可以从PHP中调用,如下所示:
#header("Content-Type: archive/zip");
passthru("zip -0 -q -r - *.*");
这将导致一个未压缩的zip文件直接发送回客户端
如果这没有帮助,那么MacOS zip前端可能不喜欢未压缩的内容。请删除-0
标志。问题在于“需要解压缩的版本”字段,我通过对ZipStream创建的文件与InfoZip创建的文件进行十六进制差异,并分析差异,试图解决它们
ZipStream默认将其设置为0x0603。Info zip将其设置为0x000A。具有前一个值的zip文件似乎不会在存档实用程序中打开。可能它不支持该版本的功能
强制将“需要提取的版本”设置为0x000A会使生成的文件在Archive Utility中打开,就像在其他地方一样
编辑:此问题的另一个原因是zip文件是使用Safari(用户代理版本>=537)下载的,并且您在发送内容长度标题时低估了文件大小
我们采用的解决方案是检测Safari>=537服务器端,如果您使用的是Safari,我们将确定内容长度大小和实际大小之间的差异(这取决于您的具体应用程序),并在调用$zipStream->finish()后,回显chr(0)以达到正确的长度。生成的文件在技术上是错误的,您在zip中的任何注释都不会显示,但所有zip程序都可以打开它并提取文件
如果你误报了你的内容长度,IE也需要同样的技巧,但不是下载一个不起作用的文件,它只是不会完成下载并抛出一个“下载中断”。我在Windows和Linux上使用的InfoZip命令行工具使用版本20作为zip的“需要解压缩的版本”字段。这在PHP上也是必需的,因为默认压缩是Deflate算法。因此“需要提取的版本”字段实际上应该是0x0014。如果更改“(6使用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);
}
我确实遇到过这个问题,但原因不同 在我的例子中,php生成的zip将从命令行打开,但不会通过OSX中的finder打开 我犯了一个错误,在创建zip文件并将其作为响应发送回之前,允许一些HTML内容进入输出缓冲区
<some html></....>
<?php
// Output a zip file...
对于在Symfony中使用ZipStream的用户,以下是您的解决方案:
如果您的控制器操作响应不是StreamdResponse,您可能会得到一个损坏的包含html的zip文件,正如我发现的那样。这是一个老问题,但我留下了它对我有用的内容,以防它对其他人有所帮助。
设置选项时,需要将Zero header设置为true,并将enable zip 64设置为false(这会将存档限制为4 Gb):
其他一切如所述。
在passthru上找到的解决方案是一个好主意,如果没有其他办法。谢谢!使用该库的好处是文件实际上托管在一个单独的服务器上,我在“压缩”之前暂时将它们拉下来。使用“压缩”要求在发送任何数据之前将所有文件拉下来,给用户一个很长的时间,“这个网站坏了吗?”?“在任何明显的事情发生之前等待,而我自己创建的zip让我能够在文件传入时传递它们,让它们看起来不断进步。啊,好吧。那是一个很大的困难。这需要一个过于复杂的解决方法(nfs、sshfs或davfs)也许你应该试着为zipstream类启用压缩,至少是为了测试。
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;
}
$options->setZeroHeader(true);
$opt->setEnableZip64(false)