PHP Conference Japan 2024

gzopen

(PHP 4, PHP 5, PHP 7, PHP 8)

gzopen打开 gz 文件

描述

gzopen(字符串 $filename, 字符串 $mode, 整数 $use_include_path = 0): 资源|false

打开一个 gzip (.gz) 文件以进行读取或写入。

gzopen() 可用于读取不是 gzip 格式的文件;在这种情况下,gzread() 将直接从文件读取而无需解压缩。

参数

filename

文件名。

mode

fopen() 中相同 (rbwb),但也可以包含压缩级别 (wb9) 或策略:f 表示过滤数据,如 wb6f 中所示,h 表示 仅霍夫曼压缩,如 wb1h 中所示。(有关策略参数的更多信息,请参阅 zlib.hdeflateInit2 的描述。)

use_include_path

如果希望在 include_path 中搜索文件,则可以将此可选参数设置为 1

返回值

返回打开的文件的指针,之后,您从此文件描述符读取的所有内容都将被透明地解压缩,并且您写入的内容将被压缩。

如果打开失败,则函数返回 false

示例

示例 #1 gzopen() 示例

<?php
$fp
= gzopen("/tmp/file.gz", "r");
?>

参见

  • gzclose() - 关闭打开的 gz 文件指针

添加注释

用户贡献的注释 9 条注释

David Gero dave at havidave dot com
13 年前
gzopen("php://output","wb") 在 Web 服务器上不起作用,fopen("compress.zlib://php://output","wb") 也不起作用。

这是一个代码片段,用于压缩文件并在运行时输出它,无需使用临时文件,无需将文件读入内存,也无需多次读取文件



<?php
$fin
= fopen($file, "rb");
if (
$fin !== FALSE) {
$fout = fopen("php://output", "wb");
if (
$fout !== FALSE) {
// 写入 gzip 头部
fwrite($fout, "\x1F\x8B\x08\x08".pack("V", filemtime($file))."\0\xFF", 10);
// 写入原始文件名
$oname = str_replace("\0", "", basename($file));
fwrite($fout, $oname."\0", 1+strlen($oname));
// 使用默认压缩级别添加 deflate 过滤器
$fltr = stream_filter_append($fout, "zlib.deflate", STREAM_FILTER_WRITE, -1);
// 设置 CRC32 哈希上下文
$hctx = hash_init("crc32b");
// 关闭时间限制
if (!ini_get("safe_mode")) set_time_limit(0);
$con = TRUE;
$fsize = 0;
while ((
$con !== FALSE) && !feof($fin)) {
// deflate 最适合缓冲区 >32K
$con = fread($fin, 64 * 1024);
if (
$con !== FALSE) {
hash_update($hctx, $con);
$clen = strlen($con);
$fsize += $clen;
fwrite($fout, $con, $clen);
}
}
// 删除 deflate 过滤器
stream_filter_remove($fltr);
// 写入 CRC32 值
// hash_final 是一个字符串,而不是整数
$crc = hash_final($hctx, TRUE);
// 需要反转 hash_final 字符串,使其为小端序
fwrite($fout, $crc[3].$crc[2].$crc[1].$crc[0], 4);
// 写入原始未压缩文件大小
fwrite($fout, pack("V", $fsize), 4);
fclose($fout);
}
fclose($fin);
}
?>
[email protected]
16 年前
警告 gzopen 和 gzread 存在一个主要缺点。它们不进行任何校验和和长度验证,并且丢弃这些有价值的信息。这应该在此处记录。
[email protected]
9 年前
David Gero 的优秀 GZ 文件格式说明的 OO 版本,也在此页面上

<?php

// David Gero 的示例:读取文件并输出 GZ
$gz_to_out = new GZTempFile(basename($file), "php://output");
$fin = fopen($file, "rb");
while(
$data = fread($fin, 64 * 1024)) {
$gz_to_out->fwrite();
}
close($fin);
$gz_to_out->flushBuffer();

// 动态构建 GZ 文件内容的示例(临时文件句柄)
$gz_custom = new GZTempFile();
foreach ( ... ) {
// 一些操作
$str = ...

$gz_custom->fwrite($str);
}
// 将其存储到数据库中
$m = new MongoClient();
$gridfs = $m->selectDB('test')->getGridFS();
$id = $gridfs->storeFile($gz_custom->getReadFilehandle(), array('contentType' => 'application/x-gzip'));

class
GZTempFile {
private
$__fh = null;
public
$uncompressed_bytes = 0;
public
$filesize = null;
private
$gz_filter = null;
private
$file_hash = null;
private
$final_read_fh = false;
private
$__buffer = '';
private
$__buffer_len = 0;

public function
__construct($filename = 'data', $fh = null) {
$this->__fh = is_null($fh) ? fopen('php://temp','w+') : $fh;
fwrite($this->__fh, "\x1F\x8B\x08\x08".pack("V", time())."\0\xFF", 10); // GZ 文件头
fwrite($this->__fh, str_replace("\0", "", basename($filename)) ."\0"); // GZ 文件名 = data,需要吗?
$this->gz_filter = stream_filter_append($this->__fh, "zlib.deflate", STREAM_FILTER_WRITE, -1);
$this->uncompressed_bytes = 0;
$this->file_hash = hash_init("crc32b");
}

public function
fwrite($str,$length = null) {
if (
$this->final_read_fh ) { throw new Exception("GZTempFile 已经完成并关闭。无法再写入"); }
hash_update($this->file_hash, $str);
$this->uncompressed_bytes += strlen($str);
$this->__buffer_len += strlen($str);
$this->__buffer .= $str;
if (
$this->__buffer_len >= 64 * 1024 ) { $this->flushBuffer(); }
}
public function
flushBuffer() {
if (
$this->__buffer_len == 0 ) { return false; }
$return = fwrite($this->__fh, $this->__buffer);
$this->__buffer_len = 0;
$this->__buffer = '';
return
$return;
}

public function
getReadFilehandle() {
if ( !
$this->final_read_fh ) {
$this->flushBuffer();
stream_filter_remove($this->gz_filter);
$crc = hash_final($this->file_hash, TRUE); // hash_final 是字符串,而不是整数
fwrite($this->__fh, $crc[3].$crc[2].$crc[1].$crc[0]); // 需要反转 hash_final 字符串,使其为小端序
fwrite($this->__fh, pack("V", $this->uncompressed_bytes), 4);

$this->filesize = ftell($this->__fh);
rewind($this->__fh);
$this->final_read_fh = $this->__fh;
}
return
$this->final_read_fh;
}
}
[email protected]
5 年前
有一个关于 xzopen 函数的活跃功能请求。xz 程序使用 LZMA 算法,其压缩率远高于 gzip。

在 xzopen 可能可用之前,以下是我安全读取远程来源且不受信任的 .xz 文件的最佳设计。

下面是一个 CSV 日志查看器 - 这里有很多内容,我将在下面进行解释

<?

$PRG_NAME = basename($_SERVER['PHP_SELF'], '.php');
$PRG_LEN = strlen($PRG_NAME);

if(substr($z = $_GET['CSV'], 0, $PRG_LEN) == $PRG_NAME)
{
header('content-type:application/csv;charset=UTF-8');
header('Content-Disposition:attachment;filename="' .
$_GET['CSV'] . '.csv"');

if('.xz' == substr($z, -3))
{
$tmpfname = tempnam("/tmp", "php-log-viewer");
$fpx = fopen($tmpfname, 'w');
fprintf($fpx, "/var/app/log/%s\0", $z);
fclose($fpx);

$fp = popen("xz -cd --files0=" . $tmpfname, 'r');
}
else $fp = fopen('/var/app/log/' . $z, 'r');

while($line = fgets($fp))
echo '"' . preg_replace('/[~]/', '","', rtrim($line)) . "\"\n";

fclose($fp); if(is_file($tmpfname)) unlink($tmpfname); exit;


}

?>

日志文件是以波浪号分隔符(~)分隔的文件,位于 /var/app/log 目录下,上面代码将其转换为 CSV 并注入到 Excel 中。它们会定期由 cron 压缩,但最新的日志将是未压缩的文本文件。我的代码的另一部分(此处未包含)通过 opendir()/readdir()/stat() 展示它们。

查看器允许用户查看的文件前缀由脚本名称决定 - 如果我将其命名为 FTP-LOG.php,则可以读取任何以 /var/app/log/FTP-LOG 开头的文件。我通过为脚本创建硬链接来为不同的前缀启用查看器。

由于日志可能(尚未)被压缩,我检查扩展名 - 如果检测到 .xz,则开始进行一系列操作。

将远程用户的表单内容传递给 UNIX shell 不安全,我正在尝试避免这种情况。幸运的是,xz 具有 --files 和 --files0 选项,我创建一个临时文件名,在其中记录目标文件,然后打开一个 xz 进程进行读取(否则,一个简单的 fopen() 就足够了)。记录一个 \0 允许更安全地处理包含嵌入换行符的文件(POSIX 允许),并且对于“find -print0”和“xargs -0”的粉丝来说非常熟悉。

不幸的是,bzip2 和 lzip 都没有 --files[0] 选项。在这种情况下它非常有用,并且似乎提高了安全性。
[email protected]
19 年前
根据我的测试,[email protected] 的评论是不正确的。该代码希望在解析之前下载整个文件,这很不方便。wget 方法可以工作。
[email protected]
19 年前
请注意,当在 http 服务器上打开远程文件时,gzopen 在等待任何响应 120 秒后默认会返回 false。
[email protected]
20 年前
关于 dubious 的评论:“能够从 ftp 和 http 读取 gzip 流是我目前个人愿望清单中的首要任务……”

一种通过 http 读取 gzip 流的方法是串联流包装器,例如:

<?
$fp = fopen("compress.zlib://http://some.website.org/example.gz", "r");
?>
[email protected]
22 年前
“动态”解压缩实际上确实可以工作 - 似乎出于某种原因,只能访问本地流/文件(包括 php://stdin)。我认为(但尚未测试)您可以类似地 gzopen “php://stdout” 并通过那里将 gzip 数据流传递给浏览器(从网页运行)或控制台(从独立运行)。

我已经测试了从命令行运行的脚本,例如

wget -q -O- ftp://some.host.net/pub/some_gzip_file.gz | php gunzip_stuff.php

其中 gunzip_stuff.php 将是一个脚本,它 gzopened “php://stdin” 并从该流中执行 gzgets,它似乎工作正常,但这显然无助于想要从基于 Web 的脚本中获取远程站点的 gzip 流的人。

能够从 ftp 和 http 读取 gzip 流是我目前个人愿望清单中的首要任务……
plasma
16 年前
这在我的高负载(每秒 50 多个文件)下工作不稳定

<?php
$gz
= gzopen ( $file, 'w9' );
gzwrite ( $gz, $content );
gzclose ( $gz );
?>

以下工作正常

<?php
$f
= fopen ( $file, 'w' );
fwrite ( $f, gzcompress ( $content, 9 ) );
fclose ( $f );
?>
To Top