gzopen
(PHP 4, PHP 5, PHP 7, PHP 8)
gzopen — gz ファイルを開く
説明
読み込みもしくは書き込みのために gzip (.gz) ファイルをオープンします。
gzopen() は、gzip フォーマットでないファイルの読み込みについても使用することができます。 この場合、gzread()は、ファイルを解凍せずに直接読み 込まれます。
パラメータ
filename
-
ファイル名を指定します
mode
-
fopen() と同じ (
rb
またはwb
) ですが、 圧縮レベル (wb9
) または圧縮の方策、 つまり、wb6f
のようにフィルターを通したデータをf
で指定したり、h
でハフマン圧縮のみを行うことを指定したりすることができます (方策に関するパラメータの詳細については、zlib.h の中のdeflateInit2
の説明を参照ください)。 use_include_path
-
このオプションパラメータを
1
にすることにより、 include_pathにあるファイルも 検索することができます。
戻り値
オープンしたファイルへのファイルポインタを返します。その後、 このファイルディスクプリタから読み込んだ全ては透過的に解凍され、 書き込んだものは圧縮されます。
オープンに失敗した場合、この関数は false
を返します。
例
例1 gzopen() の例
<?php
$fp = gzopen("/tmp/file.gz", "r");
?>
+add a note
User Contributed Notes 9 notes
David Gero dave at havidave dot com ¶
13 years ago
gzopen("php://output","wb") doesn't work on a web server, nor does fopen("compress.zlib://php://output","wb").
Here is a snippet to gzip a file and output it on the fly, without using a temporary file, without reading the file into memory, and without reading the file more than once:
<?php
$fin = fopen($file, "rb");
if ($fin !== FALSE) {
$fout = fopen("php://output", "wb");
if ($fout !== FALSE) {
// write gzip header
fwrite($fout, "\x1F\x8B\x08\x08".pack("V", filemtime($file))."\0\xFF", 10);
// write the original file name
$oname = str_replace("\0", "", basename($file));
fwrite($fout, $oname."\0", 1+strlen($oname));
// add the deflate filter using default compression level
$fltr = stream_filter_append($fout, "zlib.deflate", STREAM_FILTER_WRITE, -1);
// set up the CRC32 hashing context
$hctx = hash_init("crc32b");
// turn off the time limit
if (!ini_get("safe_mode")) set_time_limit(0);
$con = TRUE;
$fsize = 0;
while (($con !== FALSE) && !feof($fin)) {
// deflate works best with buffers >32K
$con = fread($fin, 64 * 1024);
if ($con !== FALSE) {
hash_update($hctx, $con);
$clen = strlen($con);
$fsize += $clen;
fwrite($fout, $con, $clen);
}
}
// remove the deflate filter
stream_filter_remove($fltr);
// write the CRC32 value
// hash_final is a string, not an integer
$crc = hash_final($hctx, TRUE);
// need to reverse the hash_final string so it's little endian
fwrite($fout, $crc[3].$crc[2].$crc[1].$crc[0], 4);
// write the original uncompressed file size
fwrite($fout, pack("V", $fsize), 4);
fclose($fout);
}
fclose($fin);
}
?>
katzlbtjunk at hotmail dot com ¶
16 years ago
WARNING gzopen and gzread have a major disadvantage. They makes NO checksum and NO length verification of the gzipped data and discard this valuable information. This should be documented here.
dj8 at joesvolcano dot net ¶
9 years ago
An OO-version of David Gero's excellent GZ File format note also on this page:
<?php
// David Gero's example: read a file and output 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();
// Example of building your GZ file content on the fly (temp filehandle)
$gz_custom = new GZTempFile();
foreach ( ... ) {
// Some work
$str = ...
$gz_custom->fwrite($str);
}
// Store it in a database
$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 file header
fwrite($this->__fh, str_replace("\0", "", basename($filename)) ."\0"); // GZ filename = data, needed???
$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 has already been finalized and closed. No more writing"); }
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 is a string, not an integer
fwrite($this->__fh, $crc[3].$crc[2].$crc[1].$crc[0]); // need to reverse the hash_final string so it's little endian
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;
}
}
chasjfish at gmail dot com ¶
5 years ago
There is an active feature request for an xzopen function. The xz program uses the LZMA algorithm for much higher compression ratios than gzip.
Until such time that xzopen might be available, here is my best design for safely reading .xz files which originate remotely and are not trusted.
Below is a CSV log viewer - there is a lot going on here, so I will explain it below:
<?
$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;
}
?>
The logs are tilde-delimited files (~) in the /var/app/log directory which the code above converts to CSV and injects into Excel. They are regularly compressed by cron, but the latest logs will be uncompressed text files. A separate section of my code (not included here) presents them via opendir()/readdir()/stat().
The file prefix that the viewer will allow the user to see is determined by the name of the script - if I name it FTP-LOG.php, then any file beginning with /var/app/log/FTP-LOG can be read. I am enabling the viewer for different prefixes by making hard links to the script.
Since the log might not (yet) be compressed, I check the extension - if .xz is detected, then the gymnastics begin.
It is not safe to pass form content from remote users to a UNIX shell, and I am trying to avoid this. Fortunately, xz has the --files and --files0 options, and I create a temporary filename, record the file of interest in it, then open an xz process for reading (otherwise, a simple fopen() will suffice). Recording a \0 allows safer processing of files with embedded newlines (which is allowed by POSIX), and is immediately familiar to fans of "find -print0" and "xargs -0".
Unfortunately, neither bzip2 nor lzip have have a --files[0] option. It is quite useful in this case, and appears to improve security.
rob at digital-crocus dot com ¶
19 years ago
dtorop932 at hotmail dot com's comments, according to my tests, is incorrect. That code wishes to download the entire file before parsing, which is inconvinient. The wget method works though.
pentek_imre at mailbox dot hu ¶
19 years ago
Be aware that when opening a remote file on a http server the gzopen will return by default false after 120 seconds waiting to any answer.
dtorop932 at hotmail dot com ¶
20 years ago
RE dubious's comment: "Being able to read gzip streams from ftp and http is near the top of my personal wishlist at the moment..."
One way to read a gzip stream over http is to daisychain stream wrappers, e.g.:
<?
$fp = fopen("compress.zlib://http://some.website.org/example.gz", "r");
?>
-delete-this-part-dubious at 2xtreme dot net ¶
22 years ago
"On the fly" gunzipping actually DOES seem to work - it just appears that only LOCAL streams/files (including php://stdin) can be accessed for some reason. I THINK (but have not yet tested) that you could similarly gzopen "php://stdout" and pass a stream of gzipped data to the browser (when run from a web page) or console (when run standalone) through there.
I HAVE tested scripts from the command line like:
wget -q -O- ftp://some.host.net/pub/some_gzip_file.gz | php gunzip_stuff.php
where gunzip_stuff.php would be a script that gzopened "php://stdin" and did gzgets from that stream, and it seems to work fine, but that obviously doesn't help someone wanting to grab gzipped streams from remote sites from a web-based script.
Being able to read gzip streams from ftp and http is near the top of my personal wishlist at the moment...