請注意,Zip 函數在發生錯誤時會返回一個整數錯誤代碼。所以
<?php
$zip = zip_open($file);
if ($zip) {
?>
是不正確的。應該使用
<?php
$zip = zip_open($file);
if (is_resource($zip)) {
?>
(PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PHP 7, PHP 8, PECL zip >= 1.0.0)
zip_open — 開啟 ZIP 檔案壓縮檔
此函式自 PHP 8.0.0 起已被 _棄用_。強烈建議不要依賴此函式。
filename
要開啟的 ZIP 壓縮檔的檔名。
傳回一個資源控制代碼,供後續與 zip_read() 和 zip_close() 使用,或者如果 filename
不存在或發生其他錯誤時,返回 false
或錯誤代碼。
請注意,Zip 函數在發生錯誤時會返回一個整數錯誤代碼。所以
<?php
$zip = zip_open($file);
if ($zip) {
?>
是不正確的。應該使用
<?php
$zip = zip_open($file);
if (is_resource($zip)) {
?>
一些較舊的 PHP 版本在 zip_open 失敗時會返回 false,而較新的版本會返回錯誤代碼(整數),因此不要使用
$zip = zip_open($zip_file);
if ($zip) {
// 認為 zip 檔案已成功開啟
}
而應該使用
$zip = zip_open($zip_file);
if (is_resource($zip)) {
// 認為 zip 檔案已成功開啟
}
您也可以使用以下函數根據錯誤代碼取得錯誤訊息
function zipFileErrMsg($errno) {
// 使用常數名稱作為字串以使此函數與 PHP4 相容
$zipFileFunctionsErrors = array(
'ZIPARCHIVE::ER_MULTIDISK' => '不支援多磁片 zip 檔案。',
'ZIPARCHIVE::ER_RENAME' => '重新命名暫存檔失敗。',
'ZIPARCHIVE::ER_CLOSE' => '關閉 zip 檔案失敗。',
'ZIPARCHIVE::ER_SEEK' => '搜尋錯誤。',
'ZIPARCHIVE::ER_READ' => '讀取錯誤。',
'ZIPARCHIVE::ER_WRITE' => '寫入錯誤。',
'ZIPARCHIVE::ER_CRC' => 'CRC 錯誤。',
'ZIPARCHIVE::ER_ZIPCLOSED' => '包含的 zip 檔案已關閉。',
'ZIPARCHIVE::ER_NOENT' => '找不到檔案。',
'ZIPARCHIVE::ER_EXISTS' => '檔案已存在。',
'ZIPARCHIVE::ER_OPEN' => '無法開啟檔案。',
'ZIPARCHIVE::ER_TMPOPEN' => '建立暫存檔失敗。',
'ZIPARCHIVE::ER_ZLIB' => 'Zlib 錯誤。',
'ZIPARCHIVE::ER_MEMORY' => '記憶體配置失敗。',
'ZIPARCHIVE::ER_CHANGED' => '項目已變更。',
'ZIPARCHIVE::ER_COMPNOTSUPP' => '不支援的壓縮方法。',
'ZIPARCHIVE::ER_EOF' => '提前出現 EOF。',
'ZIPARCHIVE::ER_INVAL' => '無效的參數。',
'ZIPARCHIVE::ER_NOZIP' => '不是 zip 檔案。',
'ZIPARCHIVE::ER_INTERNAL' => '內部錯誤。',
'ZIPARCHIVE::ER_INCONS' => 'Zip 檔案不一致。',
'ZIPARCHIVE::ER_REMOVE' => '無法移除檔案。',
'ZIPARCHIVE::ER_DELETED' => '項目已刪除',
);
$errmsg = '未知';
foreach ($zipFileFunctionsErrors as $constName => $errorMessage) {
if (defined($constName) and constant($constName) === $errno) {
return 'Zip 檔案函式錯誤:'.$errorMessage;
}
}
return 'Zip 檔案函式錯誤:未知';
}
$zip = zip_open($zip_file);
if (!is_resource($zip)) {
die(zipFileErrMsg($zip));
}
取得 Mozilla 附加元件版本(例如 Firefox 擴充功能)
<?php
function get_addon_version($path)
{
// 開啟 zip 檔案
$zip = zip_open($path);
// 尋找項目
do {
$entry = zip_read($zip);
} while ($entry && zip_entry_name($entry) != "install.rdf");
// 開啟項目
zip_entry_open($zip, $entry, "r");
// 讀取項目
$entry_content = zip_entry_read($entry, zip_entry_filesize($entry));
// <em:version> 的位置
$version_open_pos = strpos($entry_content, "<em:version>");
// </em:version> 的位置
$version_close_pos = strpos($entry_content, "</em:version>", $version_open_pos);
// 版本
$version = substr(
$entry_content,
$version_open_pos + strlen("<em:version>"),
$version_close_pos - ($version_open_pos + strlen("<em:version>"))
);
// 關閉項目
zip_entry_close($entry);
// 關閉 zip 檔案
zip_close($zip);
return $version;
}
?>
如果您的 PHP 安裝沒有 zip_open 函式,而且您因為某些原因無法安裝它,您可以改用這些函式,前提是伺服器可以存取「unzip」工具程式(大多數 Linux 系統都可以)。
到目前為止,我只在 Fedora Core 3 中測試過這些函式。
使用風險自負。
<?php
function ShellFix($s)
{
return "'".str_replace("'", "'\''", $s)."'";
}
function zip_open($s)
{
$fp = @fopen($s, 'rb');
if(!$fp) return false;
$lines = Array();
$cmd = 'unzip -v '.shellfix($s);
exec($cmd, $lines);
$contents = Array();
$ok=false;
foreach($lines as $line)
{
if($line[0]=='-') { $ok=!$ok; continue; }
if(!$ok) continue;
$length = (int)$line;
$fn = trim(substr($line,58));
$contents[] = Array('name' => $fn, 'length' => $length);
}
return
Array('fp' => $fp,
'name' => $s,
'contents' => $contents,
'pointer' => -1);
}
function zip_read(&$fp)
{
if(!$fp) return false;
$next = $fp['pointer'] + 1;
if($next >= count($fp['contents'])) return false;
$fp['pointer'] = $next;
return $fp['contents'][$next];
}
function zip_entry_name(&$res)
{
if(!$res) return false;
return $res['name'];
}
function zip_entry_filesize(&$res)
{
if(!$res) return false;
return $res['length'];
}
function zip_entry_open(&$fp, &$res)
{
if(!$res) return false;
$cmd = 'unzip -p '.shellfix($fp['name']).' '.shellfix($res['name']);
$res['fp'] = popen($cmd, 'r');
return !!$res['fp'];
}
function zip_entry_read(&$res, $nbytes)
{
return fread($res['fp'], $nbytes);
}
function zip_entry_close(&$res)
{
fclose($res['fp']);
unset($res['fp']);
}
function zip_close(&$fp)
{
fclose($fp['fp']);
}
?>
上面的 zip_entry_read 是錯誤的。由於檔案是用 popen 開啟的,您必須分塊讀取檔案,因此 zip_entry_read 應該要這樣寫
function zip_entry_read(&$res, $nbytes)
{
$contents = '';
while (!feof($res['fp'])) {
$contents .= fread($res['fp'], 8192);
}
return $contents;
}
除此之外,這是一個非常實用的函式庫。謝謝!
在呼叫 ZipArchive::close() 之前,zip 壓縮檔並不會儲存到磁碟上。(如果您沒有撰寫該程式碼,ZipArchive::close() 將會在腳本結束時自動呼叫。)
如果您想在將檔案新增到 zip 壓縮檔後刪除該檔案,則應該在呼叫 ZipArchive::close() 之後再刪除。否則,檔案會在實際新增到壓縮檔之前就被刪除,這將導致您的 zip 壓縮檔儲存失敗。
<?php
$za = new ZipArchive();
$za->open('./xc.zip', ZipArchive::CREATE|ZipArchive::OVERWRITE);
$file = './notes.txt';
if( true === $za->addFile($file)){
unlink($file);
}
if(!$za->close()){
echo 'failure.';
}
?>
對於 bisqwit at iki dot fi 提出的替代函式方案,在嘗試讀取檔案時只有一個問題,這是因為 fread 在處理 popen 時的錯誤,所以它只載入了 8192 位元組,以下是修正後的函式。
<?php
function zip_entry_read(&$res, $nbytes)
{
while ($s = fgets($res['fp'],1024)) {
$data .= $s;
}
return $data;
}
?>
@bisqwit at iki dot fi:如果您使用的是舊版本的 PHP,這個腳本可能只會讀取前 8192 個位元組。無論如何,這真是個好東西!
由「bisqwit at iki dot fi」編寫的 zip_entry_read() 函式必須修改才能讀取整個檔案
<?php
function zip_entry_read(&$res, $nbytes) {
while (!feof($res['fp'])) {
$contents .= fread($res['fp'], $nbytes);
}
return $contents;
}
?>
使用這個類別來建立、讀取資訊或解壓縮 ZIP 檔案。
參考 http://ajaxbrowser.free.fr/ 上的 EasyArchive.class.php 來管理 ZIP、GZIP、BZIP 和 TAR 檔案。
<?
$ARCHIVE = new zip;
$ARCHIVE->makeZip('./','./toto.zip'); // 建立一個 ZIP 檔案
var_export($ARCHIVE->infosZip('./toto.zip'), false); // 取得此 ZIP 檔案的資訊(不包含檔案內容)
var_export($ARCHIVE->infosZip('./toto.zip')); // 取得此 ZIP 檔案的資訊(包含檔案內容)
$ARCHIVE->extractZip('./toto.zip', './1/'); // 解壓縮 ZIP 檔案
class zip
{
public function infosZip ($src, $data=true)
{
if (($zip = zip_open(realpath($src))))
{
while (($zip_entry = zip_read($zip)))
{
$path = zip_entry_name($zip_entry);
if (zip_entry_open($zip, $zip_entry, "r"))
{
$content[$path] = array (
'Ratio' => zip_entry_filesize($zip_entry) ? round(100-zip_entry_compressedsize($zip_entry) / zip_entry_filesize($zip_entry)*100, 1) : false, // 壓縮比率
'Size' => zip_entry_compressedsize($zip_entry), // 壓縮後大小
'NormalSize' => zip_entry_filesize($zip_entry)); // 原始大小
if ($data)
$content[$path]['Data'] = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry)); // 檔案資料
zip_entry_close($zip_entry);
}
else
$content[$path] = false;
}
zip_close($zip);
return $content;
}
return false;
}
public function extractZip ($src, $dest)
{
$zip = new ZipArchive;
if ($zip->open($src)===true)
{
$zip->extractTo($dest);
$zip->close();
return true;
}
return false;
}
public function makeZip ($src, $dest)
{
$zip = new ZipArchive;
$src = is_array($src) ? $src : array($src);
if ($zip->open($dest, ZipArchive::CREATE) === true)
{
foreach ($src as $item)
if (file_exists($item))
$this->addZipItem($zip, realpath(dirname($item)).'/', realpath($item).'/');
$zip->close();
return true;
}
return false;
}
private function addZipItem ($zip, $racine, $dir)
{
if (is_dir($dir))
{
$zip->addEmptyDir(str_replace($racine, '', $dir));
$lst = scandir($dir);
array_shift($lst);
array_shift($lst);
foreach ($lst as $item)
$this->addZipItem($zip, $racine, $dir.$item.(is_dir($dir.$item)?'/':''));
}
elseif (is_file($dir))
$zip->addFile($dir, str_replace($racine, '', $dir));
}
}
?>
如果您只想解壓縮 zip 檔案中的特定檔案,可以使用這個函式,或許會有幫助。
<?php
/**
* This method unzips a directory within a zip-archive
*
* @author Florian 'x!sign.dll' Wolf
* @license LGPL v2 or later
* @link http://www.xsigndll.de
* @link http://www.clansuite.com
*/
function extractZip( $zipFile = '', $dirFromZip = '' )
{
define(DIRECTORY_SEPARATOR, '/');
$zipDir = getcwd() . DIRECTORY_SEPARATOR;
$zip = zip_open($zipDir.$zipFile);
if ($zip)
{
while ($zip_entry = zip_read($zip))
{
$completePath = $zipDir . dirname(zip_entry_name($zip_entry));
$completeName = $zipDir . zip_entry_name($zip_entry);
// Walk through path to create non existing directories
// This won't apply to empty directories ! They are created further below
if(!file_exists($completePath) && preg_match( '#^' . $dirFromZip .'.*#', dirname(zip_entry_name($zip_entry)) ) )
{
$tmp = '';
foreach(explode('/',$completePath) AS $k)
{
$tmp .= $k.'/';
if(!file_exists($tmp) )
{
@mkdir($tmp, 0777);
}
}
}
if (zip_entry_open($zip, $zip_entry, "r"))
{
if( preg_match( '#^' . $dirFromZip .'.*#', dirname(zip_entry_name($zip_entry)) ) )
{
if ($fd = @fopen($completeName, 'w+'))
{
fwrite($fd, zip_entry_read($zip_entry, zip_entry_filesize($zip_entry)));
fclose($fd);
}
else
{
// We think this was an empty directory
mkdir($completeName, 0777);
}
zip_entry_close($zip_entry);
}
}
}
zip_close($zip);
}
return true;
}
// The call to exctract a path within the zip file
extractZip( 'clansuite.zip', 'core/filters' );
?>
我的基本解壓縮 zip 檔案函式
<?php
函式 ezip($zip, $hedef = '')
{
$root = $_SERVER['DOCUMENT_ROOT'];
$zip = zip_open($root . $zip);
while($zip_icerik = zip_read($zip)):
$zip_dosya = zip_entry_name($zip_icerik);
if(strpos($zip_dosya, '.')):
$hedef_yol = $root . $hedef . 'x/'.$zip_dosya;
touch($hedef_yol);
$yeni_dosya = fopen($hedef_yol, 'w+');
fwrite($yeni_dosya, zip_entry_read($zip_icerik));
fclose($yeni_dosya);
else:
@mkdir($root . $hedef . 'x/'.$zip_dosya);
endif;
endwhile;
}
ezip('files.zip', 'unzip_files/');
?>