我有一个包含大量媒体文件的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
答案 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
上找到了解决方案