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 代表已過濾的數據,如 wb6fh 代表僅使用霍夫曼編碼壓縮,如 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") 在網頁伺服器上無法運作,fopen("compress.zlib://php://output","wb") 也是一樣。

以下程式碼片段可以即時壓縮檔案並輸出,無需使用暫存檔,無需將檔案讀入記憶體,也無需多次讀取檔案

<?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 年前
警告:gzopen 和 gzread 有個主要缺點。它們不會執行 gzip 資料的校驗和與長度驗證,並且會捨棄這些寶貴的資訊。這應該在此文件中說明。
dj8 at joesvolcano dot net
9 年前
此頁面上 David Gero 優秀的 GZ 檔案格式註釋的物件導向版本

<?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 年前
目前有一個關於 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] 選項。在這種情況下,它非常有用,而且似乎可以提高安全性。
rob at digital-crocus dot com
19 年前
根據我的測試,dtorop932 at hotmail dot com 的評論是不正確的。該程式碼希望在解析之前下載整個檔案,這很不方便。wget 方法有效。
pentek_imre at mailbox dot hu
19 年前
請注意,在 http 伺服器上開啟遠端檔案時,如果等待任何回應超過 120 秒,gzopen 預設會返回 false。
dtorop932 at hotmail dot com
20 年前
關於 dubious 的評論:「能夠從 ftp 和 http 讀取 gzip 串流是我目前個人願望清單的首位...」

透過 http 讀取 gzip 串流的一種方法是串接串流包裝器,例如:

<?
$fp = fopen("compress.zlib://http://some.website.org/example.gz", "r");
?>
-delete-this-part-dubious at 2xtreme dot net
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 的腳本,它似乎工作正常,但顯然這對想要從基於網路的腳本抓取遠端站點的 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