gzopen

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

gzopen打开 gz 文件

描述

gzopen(string $filename, string $mode, int $use_include_path = 0): resource|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 个注释

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

以下代码片段演示了如何在不使用临时文件、不将文件读入内存以及不读取文件超过一次的情况下,对文件进行 gzip 压缩并动态输出。

<?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);
}
?>
5
katzlbtjunk at hotmail dot com
16 年前
警告:gzopen 和 gzread 存在一个主要缺点。它们不会对 gzip 压缩数据进行校验和或长度验证,并且会丢弃这些有价值的信息。这一点应该在文档中说明。
3
dj8 at joesvolcano dot net
8 年前
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;
}
}
2
chasjfish at gmail dot com
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] 选项。在这种情况下非常有用,并且似乎提高了安全性。
0
rob at digital-crocus dot com
19 年前
根据我的测试,dtorop932 at hotmail dot com 的评论是不正确的。该代码希望在解析之前下载整个文件,这很不方便。但是 wget 方法有效。
0
pentek_imre at mailbox dot hu
19 年前
请注意,当在 http 服务器上打开远程文件时,gzopen 默认会在等待任何响应 120 秒后返回 false。
-2
dtorop932 at hotmail dot com
19 年前
关于 dubious 的评论:“能够从 ftp 和 http 读取 gzip 流是我目前最想实现的功能之一……”

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

<?
$fp = fopen("compress.zlib://http://some.website.org/example.gz", "r");
?>
-4
-delete-this-part-dubious at 2xtreme dot net
22 年前
“动态”解压 gzip 似乎确实有效 - 只是似乎出于某种原因,只有本地流/文件(包括 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 流是我目前最想实现的功能之一……
-3
plasma
15 年前
这在我的高负载(每秒 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