使用PHP在内存中操纵归档文件(无需在磁盘上创建临时文件)

32

我正在尝试在PHP中动态生成归档文件,并立即将其发送给用户(无需保存)。我想,既然我发送的数据是非持久性的,就没有必要在磁盘上创建文件,但是,在搜索网络时,我找不到如何实现。我也不关心文件格式。

所以问题是:

是否可以在PHP脚本中在内存中创建和操作文件归档,而不沿途创建临时文件?


这个归档文件可以只包含一个文件还是多个文件? - VolkerK
在我的特定情况下,它包含不止一个。 此外,我认为针对“任意”大的存档(按项计算)的解决方案更加有趣(在我的情况下,我可以使用临时文件,但那些必须使用受限制的文件系统访问权限的人怎么办呢?)。 - ralokt
5个回答

50
我曾经遇到同样的问题,但最终找到了一个有些不太常见的解决方案,并决定在这里分享。
我发现 phpmyadmin 中附带的非常好用的 zip.lib.php/unzip.lib.php 脚本,它们位于 "libraries" 目录下。
对于我来说,使用 zip.lib.php 解决了问题。
require_once(LIBS_DIR . 'zip.lib.php');

... 

//create the zip
$zip = new zipfile();

//add files to the zip, passing file contents, not actual files
$zip->addFile($file_content, $file_name);

...

//prepare the proper content type
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=my_archive.zip");
header("Content-Description: Files of an applicant");

//get the zip content and send it back to the browser
echo $zip->file();

这个脚本允许下载zip文件,无需将文件保存为真实文件或将zip本身保存为文件。
可惜这个功能不是更通用的PHP库的一部分。
这里是来自phpmyadmin源代码的zip.lib.php文件的链接: https://github.com/phpmyadmin/phpmyadmin/blob/RELEASE_4_5_5_1/libraries/zip.lib.php 更新: 请确保从zip.lib.php的开头删除以下检查,否则脚本会立即终止:
if (! defined('PHPMYADMIN')) {
    exit;
}
更新: 这段代码也可以在CodeIgniter项目中找到: https://github.com/patricksavalle/CodeIgniter/blob/439ac3a87a448ae6c2cbae0890c9f672efcae32d/system/helpers/zip_helper.php


你应该提到需要修改它以删除对'PHPMYADMIN'常量定义的检查,否则它将会终止脚本。我花了超过一秒钟才弄清楚这个问题。 - Nate Higgins
非常顺利,我将其转换为Codeigniter助手,没有任何问题。 - Patrick Savalle
我使用这段代码创建了一个Codeigniter助手。对我来说很有效。这是它的链接:https://github.com/patricksavalle/CodeIgniter/blob/439ac3a87a448ae6c2cbae0890c9f672efcae32d/system/helpers/zip_helper.php - Patrick Savalle
@nettle 看起来文件已经移动到 https://github.com/phpmyadmin/phpmyadmin/blob/master/libraries/ZipFile.php - Chris Belyeu
链接现在在Github上是404。 - jj-aa
显示剩余2条评论

3

1
目前,我正在使用php zip扩展,并在提供下载时编写临时文件。我尝试使用php://temp包装器(认为它比php://memory更好,因为对于大量数据,临时文件是一个好主意,并且主机可能允许其使用,即使限制fs访问。我遇到的问题是我必须关闭zip文件,这显然释放了php://temp/中的所有内容。(在关闭之前和之后从zip文件中读取都给了我0字节。)无论如何,我不关心我使用的库,只要它工作即可。当然,PHP内部库优先。 - ralokt

2

0
你需要使用 ZipArchive::addFromString - 如果你使用 addFile(),文件直到你关闭它之前才会被添加。(在我看来这是个可怕的错误,如果你试图将文件移动到 zip 中并在关闭 zip 之前删除它们,那该怎么办呢...)
addFromString() 方法会立即将其添加到归档中。

-7

这里真的存在性能问题吗,还是只是触犯了你的正义感?很多进程会写入临时文件并删除它们,通常由于缓存,它们永远不会被写入磁盘。

当关闭时,临时文件会自动删除。这就是它的本质。

我只能想到两种在内存中创建zip文件并提供服务的方法,但它们可能都不值得麻烦。

  • 使用ram磁盘。
  • 修改ziparchive类以添加一个执行close()方法所有操作(除了实际关闭文件)的方法。 (或将leave-open参数添加到close()方法中)。
    这可能取决于底层C库是否支持。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接