使用PHP + Apache高速生成ZIP文件?

时间:2009-06-13 10:10:12

标签: php apache zip

引用一些famous words

  

“程序员......经常躲避一种可以理解但却又灾难性的倾向,他们倾向于工作中的复杂性和独创性。禁止设计比节目更大的东西,他们通过使该节目错综复杂来挑战他们的专业技能来做出回应。“

在解决工作中一些平凡的问题时,我提出了这个想法,我不太清楚如何解决。我知道我不会实现这个,但我很好奇最好的解决方案是什么。 :)


假设您有一个包含JPG文件和一些奇怪的SWF文件的大集合。 “大”我的意思是“几千”。每个JPG文件大约200KB,SWF最大可达几MB。每天都有一些新的JPG文件。因此,所有东西的总大小约为1 GB,并且缓慢但稳定地增加。很少更改或删除文件。

用户可以在网页上单独查看每个文件。但是,也希望允许他们一次下载一大堆。这些文件附有一些元数据(日期,类别等),用户可以通过这些元数据过滤。

最终的实现是允许用户指定一些过滤条件,然后将相应的文件下载为单个ZIP文件。

由于标准数量足够大,我无法预先生成所有可能的ZIP文件,因此必须即时执行。另一个问题是下载量可能非常大,对于连接速度较慢的用户来说,很可能需要一个小时或更长时间。因此,支持“简历”是必须的。

然而,在光明的一面,ZIP不需要压缩任何东西 - 无论如何文件都是JPEG。因此,整个过程不应该比简单的文件下载更耗费CPU。

因此我发现的问题是:

  • PHP有脚本执行超时。虽然可以通过脚本本身进行更改,但是完全删除它会不会有问题吗?
  • 使用resume选项,可能会针对不同的HTTP请求更改筛选结果。这可以通过按时间顺序排序结果来缓解,因为集合只会变得更大。然后,请求URL还将包含最初创建日期的日期,并且脚本不会考虑比此更小的文件。这还够吗?
  • 通过PHP传递大量文件数据本身不会受到性能影响吗?

你会如何实现这个? PHP完全可以胜任这项任务吗?

<小时/> 的加了:

到目前为止,已有两个人建议将所请求的ZIP文件存储在一个临时文件夹中,并从那里作为常用文件提供服务。虽然这确实是一个显而易见的解决方案,但有几个实际考虑因素使其不可行。

ZIP文件通常非常大,从几十兆到几千兆字节。用户请求“所有内容”也是完全正常的,这意味着ZIP文件的大小将超过千兆字节。此外,还有许多可能的过滤器组合,其中许多可能由用户选择。

因此,ZIP文件的生成速度非常慢(由于数据量和磁盘速度很快),并且会多次包含整个集合。如果没有一些超级昂贵的SCSI RAID阵列,我不知道这个解决方案是如何工作的。

5 个答案:

答案 0 :(得分:9)

这可能是您所需要的: http://pablotron.org/software/zipstream-php/

此lib允许您构建动态流式压缩文件而无需交换到磁盘。

答案 1 :(得分:1)

使用例如PhpConcept Library Zip库。

除了不直接访问zip文件的情况外,您的网络服务器必须支持恢复。如果您有一个PHP脚本作为中介,那么请注意发送正确的标题以支持恢复。

创建文件的脚本不应该超时只是确保用户不能一次选择数千个文件。并保留一些东西来删除“旧的zipfiles”,并注意一些恶意用户通过请求许多不同的文件集来不会耗尽你的磁盘空间。

答案 2 :(得分:1)

如果您希望它们能够恢复下载,则必须存储生成的zip文件。

基本上你生成zip文件并将其放在/ tmp目录中,并带有可重复的文件名(可能是搜索过滤器的哈希值)。然后,您将正确的标头发送给用户,并将file_get_contents回显给用户。

为了支持恢复你需要查看$ _SERVER ['HTTP_RANGE']值,它的格式是详细的here,一旦你解析了你需要运行这样的东西。

$size = filesize($zip_file);

if(isset($_SERVER['HTTP_RANGE'])) {
    //parse http_range
    $range = explode( '-', $seek_range);
    $new_length = $range[1] - $range[0]
    header("HTTP/1.1 206 Partial Content");
    header("Content-Length: $new_length");
    header("Content-Range: bytes {$range[0]}-$range[1]");
    echo file_get_contents($zip_file, FILE_BINARY, null, $range[0], $new_length);
} else {
    header("Content-Range: bytes 0-$size");
    header("Content-Length: ".$size);
    echo file_get_contents($zip_file);
} 

这是非常粗略的代码,您可能需要稍微使用标题和HTTP_RANGE变量的内容。如果您愿意,可以使用fopen和fwrite而不是file_get内容,只需将fseek放到正确的位置即可。

现在回答你的问题

  • PHP有脚本执行超时。虽然可以通过脚本本身进行更改,但是完全删除它会不会有问题吗?

如果你愿意,你可以删除它,但是如果某些东西变成了梨形并且你的代码卡在无限循环中会导致有趣的问题,如果无限循环在某处记录和出错并且你没有注意到,直到一个相当脾气暴躁的系统管理员想知道为什么他们的服务器用完了硬盘空间;)

  • 使用resume选项,可能会针对不同的HTTP请求更改筛选结果。这可以通过按时间顺序排序结果来缓解,因为集合只会变得更大。然后,请求URL还将包含最初创建日期的日期,并且脚本不会考虑比此更小的文件。这还够吗?

将文件缓存到硬盘,意味着您不会遇到此问题。

  • 通过PHP传递大量文件数据本身不会受到性能影响吗?

是的,它不会像从网络服务器那样定期下载一样快。但它不应该太慢。

答案 3 :(得分:1)

我有一个下载页面,并制作了一个与您的想法非常相似的zip类。 我的下载是非常大的文件,无法使用zip类正确压缩。

我和你有类似的想法。 放弃压缩的方法是非常好的,你甚至不需要更少的CPU资源,你节省了内存,因为你不必触摸输入文件并可以通过它,你也可以计算像zip头一样的一切并且结尾文件化非常容易,你可以跳到每个位置并从这一点生成以实现恢复。

我更进一步,我从所有输入文件crc生成一个校验和,并将其用作生成文件的电子标签以支持缓存,并作为文件名的一部分。 如果您已经下载了生成的zip文件,则浏览器会从本地缓存而不是服务器获取它。 您还可以调整下载速率(例如300KB / s)。 一个人可以发表拉链评论。 您可以选择可以添加哪些文件以及不添加哪些文件(例如thumbs.db)。

但是完全无法用zip格式解决的一个问题。 这就是crc值的产生。 即使你使用hash-file来克服内存问题,或者使用hash-update来逐步生成crc,它也会使用很多cpu资源。 一个人不多,但不推荐专业用途。 我用额外的crc值表解决了这个问题,我用额外的脚本生成了这个表。 我将每个参数的crc值添加到zip类中。 有了这个,这个课程超快。 就像你提到的常规下载脚本一样。

我的zip课程正在进行中,您可以在此处查看:http://www.ranma.tv/zip-class.txt

我希望我能帮助那些人:)

但我会停止这种方法,我会将我的课程重新编程为tar课程。 使用tar我不需要从文件生成crc值,tar只需要一些标题的校验和,这就是全部。 我不再需要额外的mysql表了。 我认为如果你不必为它创建额外的crc表,它会使类更容易使用。 它并不那么难,因为tars文件结构比zip结构更容易。

  

PHP脚本执行超时。虽然可以通过脚本本身进行更改,但是完全删除它会不会有问题吗?

如果您的脚本是安全的并且在用户中止时关闭,那么您可以完全删除它。 但是如果你只是更新你传递的每个文件的超时时间会更安全:)

  

使用resume选项,可能会针对不同的HTTP请求更改筛选结果。这可以通过按时间顺序排序结果来缓解,因为集合只会变得更大。然后,请求URL还将包含最初创建日期的日期,并且脚本不会考虑比此更小的文件。这还够吗?

是的,这会奏效。 我从输入文件crc生成了一个校验和。 我用它作为电子标签和zip文件名的一部分。 如果更改了某些内容,则用户无法恢复生成的zip, 因为电子标签和文件名与内容一起改变了。

  

通过PHP传递大量文件数据本身不会受到性能影响吗?

不,如果你只是通过它,它将不会使用比常规下载更多。 也许0.01%我不知道,它不多:) 我假设因为php对数据没有太大作用:)

答案 4 :(得分:0)

您可以使用ZipStreamPHPZip,它会动态地将压缩文件发送到浏览器,分成块,而不是在PHP中加载整个内容然后发送zip文件。< / p>

这两个库都是很好且有用的代码片段。一些细节:

  • ZipStream&#34;工作&#34;仅限内存,但必要时无法轻松移植到PHP 4(使用hash_file()
  • PHPZip在磁盘上写入临时文件(消耗的磁盘空间与要在zip中添加的最大文件一样多),但如果需要,可以很容易地适应PHP 4。