2024 日本 PHP 研討會

ZipArchive::open

(PHP 5 >= 5.2.0, PHP 7, PHP 8, PECL zip >= 1.1.0)

ZipArchive::open開啟 ZIP 檔案

說明

public ZipArchive::open(字串 $filename, 整數 $flags = 0): 布林值|整數

開啟一個新的或現有的 zip 檔案以進行讀取、寫入或修改。

自 libzip 1.6.0 起,空檔案不再被視為有效的壓縮檔。

參數

filename

要開啟的 ZIP 壓縮檔檔名。

flags

用於開啟壓縮檔的模式。

回傳值

成功時返回 true,失敗時返回 false 或以下錯誤碼之一

ZipArchive::ER_EXISTS
檔案已存在。
ZipArchive::ER_INCONS
Zip 壓縮檔不一致。
ZipArchive::ER_INVAL
無效的參數。
ZipArchive::ER_MEMORY
記憶體配置失敗。
ZipArchive::ER_NOENT
找不到檔案。
ZipArchive::ER_NOZIP
不是 zip 壓縮檔。
ZipArchive::ER_OPEN
無法開啟檔案。
ZipArchive::ER_READ
讀取錯誤。
ZipArchive::ER_SEEK
搜尋錯誤。

範例

範例 #1 開啟並解壓縮

<?php
$zip
= new ZipArchive;
$res = $zip->open('test.zip');
if (
$res === TRUE) {
echo
'ok';
$zip->extractTo('test');
$zip->close();
} else {
echo
'失敗,代碼:' . $res;
}
?>

範例 #2 建立壓縮檔

<?php
$zip
= new ZipArchive;
$res = $zip->open('test.zip', ZipArchive::CREATE);
if (
$res === TRUE) {
$zip->addFromString('test.txt', 'file content goes here');
$zip->addFile('data.txt', 'entryname.txt');
$zip->close();
echo
'ok';
} else {
echo
'失敗';
}
?>

範例 #3 建立暫時壓縮檔

<?php
$name
= tempnam(sys_get_temp_dir(), "FOO");
$zip = new ZipArchive;
$res = $zip->open($name, ZipArchive::OVERWRITE); /* 將檔案截斷,因為空檔案無效 */
if ($res === TRUE) {
$zip->addFile('data.txt', 'entryname.txt');
$zip->close();
echo
'ok';
} else {
echo
'failed';
}
?>
新增註解

使用者貢獻的註解 20 則註解

eric at webdeveric dot com
15 年前
使用 php 5.2.6 時,以下程式碼會建立新的 zip 檔案或取代現有的 zip 檔案。
請注意,我只使用了 ZIPARCHIVE::OVERWRITE 旗標。

<?php
$zip
= new ZipArchive();
$opened = $zip->open( $zipFileName, ZIPARCHIVE::OVERWRITE );
if(
$opened !== true ){
die(
"無法開啟 {$zipFileName} 以進行寫入。");
}
$zip->addFromString( $name, $contents );
$zip->close();
?>

現在,使用 php 5.2.8 時,它無法運作並顯示以下警告

警告:ZipArchive::addFromString() [ziparchive.addfromstring]:在 [myfile] 的第 [myline] 行中出現無效或未初始化的 Zip 物件

要解決此問題,您必須將旗標指定為建立或覆寫。

<?php
$zip
= new ZipArchive();
$opened = $zip->open( $zipFileName, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE );
if(
$opened !== true ){
die(
"無法開啟 {$zipFileName} 以進行寫入。");
}
$zip->addFromString( $name, $contents );
$zip->close();
?>

在 Google 上搜尋這個錯誤訊息時,我發現很多人遇到這個問題,但卻不知道為什麼會發生。
我希望這能幫助到其他人。
abolfazl dot ziaratban at gmail dot com
9 年前
<?php
#made by abolfazl ziaratban (c)
#license GPL

class zip extends ZipArchive
{
public function
message($code)
{
switch (
$code)
{
case
0:
return
'No error';

case
1:
return
'Multi-disk zip archives not supported';

case
2:
return
'Renaming temporary file failed';

case
3:
return
'Closing zip archive failed';

case
4:
return
'Seek error';

case
5:
return
'Read error';

case
6:
return
'Write error';

case
7:
return
'CRC error';

case
8:
return
'Containing zip archive was closed';

case
9:
return
'No such file';

case
10:
return
'File already exists';

case
11:
return
'Can\'t open file';

case
12:
return
'Failure to create temporary file';

case
13:
return
'Zlib error';

case
14:
return
'Malloc failure';

case
15:
return
'Entry has been changed';

case
16:
return
'Compression method not supported';

case
17:
return
'Premature EOF';

case
18:
return
'Invalid argument';

case
19:
return
'Not a zip archive';

case
20:
return
'Internal error';

case
21:
return
'Zip archive inconsistent';

case
22:
return
'Can\'t remove file';

case
23:
return
'Entry has been deleted';

default:
return
'An unknown error has occurred('.intval($code).')';
}
}

public function
isDir($path)
{
return
substr($path,-1) == '/';
}

public function
getTree()
{
$Tree = array();
$pathArray = array();
for(
$i=0; $i<$this->numFiles; $i++)
{
$path = $this->getNameIndex($i);
$pathBySlash = array_values(explode('/',$path));
$c = count($pathBySlash);
$temp = &$Tree;
for(
$j=0; $j<$c-1; $j++)
if(isset(
$temp[$pathBySlash[$j]]))
$temp = &$temp[$pathBySlash[$j]];
else
{
$temp[$pathBySlash[$j]] = array();
$temp = &$temp[$pathBySlash[$j]];
}
if(
$this->isDir($path))
$temp[$pathBySlash[$c-1]] = array();
else
$temp[] = $pathBySlash[$c-1];
}
return
$Tree;
}
}
?>
Fred Johnson
4 年前
請注意,ZipArchive 不支援產生串流 ZIP 檔案內容(例如, dès que 一開始從資料庫之類的地方產生資料就開始將資料傳送給使用者)。這表示您必須先將整個檔案寫入磁碟,然後再將檔案傳送給使用者。根據資料量的多寡,這可能需要一些時間,而且可能會超出伺服器逾時限制。

GitHub 上有一些 PHP 使用者程式庫,可以在將任何資料寫入相關類別後立即將 ZIP 檔案內容串流傳送給使用者。

cubiclesoft/php-zipstreamwriter
maennchen/ZipStream-PHP

或許 ZipArchive 未來的版本會提供類似的功能。
jj at icglink dot com
16 年前
如果您正在輸出結果並對數字感到困惑...或許這會有幫助。雖然我不完全確定它是否準確。

ZIPARCHIVE::ER_EXISTS - 10
ZIPARCHIVE::ER_INCONS - 21
ZIPARCHIVE::ER_INVAL - 18
ZIPARCHIVE::ER_MEMORY - 14
ZIPARCHIVE::ER_NOENT - 9
ZIPARCHIVE::ER_NOZIP - 19
ZIPARCHIVE::ER_OPEN - 11
ZIPARCHIVE::ER_READ - 5
ZIPARCHIVE::ER_SEEK - 4
jekillen at prodigy net
9 年前
呼叫 ZipArchive->open() 不會建立空的 zip 壓縮檔。
我吃了不少苦頭才發現這一點。我寫的程式碼產生了正面的
結果:也就是說,從呼叫 ZipArchive 傳回的值是 TRUE
但沒有建立空的 zip 檔案。因此,在建立新的 zip 壓縮檔時,至少要呼叫
ZipArchive->addFromString(<filename.zip>, '<最小內容>')
一次。
ohcc at 163 dot com
9 年前
<?php
// 使用 ZipArchive::OVERWRITE 當目標檔案不存在時可能會導致如下錯誤
// 警告:ZipArchive::addFile(): 無效或未初始化的 Zip 物件
// 當您想要替換一個可能不存在的 zip 檔案時,請嘗試使用 ZipArchive::OVERWRITE|ZipArchive::CREATE
$zip = new ZipArchive;
$rt=$zip->open('i.zip',ZipArchive::OVERWRITE);
echo
$rt;
// 當 i.zip 不存在時,$rt 為 9,也就是 ZipArchive::ER_NOENT,或「找不到檔案」。
$zip->addFile('wuxiancheng.cn.sql','db.sql');
// 觸發錯誤,訊息為「警告:ZipArchive::addFile(): 無效或未初始化的 Zip 物件 ...」


// 使用 ZipArchive::OVERWRITE|ZipArchive::CREATE
$zip = new ZipArchive;
$zip->open('i.zip',ZipArchive::OVERWRITE|ZipArchive::CREATE);
$zip->addFile('wuxiancheng.cn.sql','db.sql');
?>
walter at clevertechie dot com
12 年前
如果您要覆寫檔案,請使用

ZIPARCHIVE::CREATE

它會覆寫現有的壓縮檔,同時如果它們不存在,則會建立新的壓縮檔。

ZIPARCHIVE::OVERWRITE 無法同時適用於這兩種情況。

(PHP 版本 5.4.4)
Jan Vavra
13 年前
http://bugs.php.net/bug.php?id=54128 中所討論,在 Windows Server 系統 (2003, 2008) 和 IIS 上,當您想要解壓縮儲存在 C:\Windows\Temp 資料夾中的檔案時會出現問題。
工作者進程的使用者 IUSR_XXX 沒有 C:\Windows\Temp 的目錄列表權限,這就是 ZipArchive::open() 失敗並顯示錯誤 11(開啟錯誤)的原因。因此,將要解壓縮的檔案儲存在 sys_get_temp_dir() 定義的資料夾中並不是一個好主意。
sunil dt bhave at gmail dt com
13 年前
即使 API 說明標誌是可選的,但我發現我必須指定標誌 ZIPARCHIVE::CREATE 才能開啟壓縮檔。
這是在 Windows 7 系統上使用 PHP 5.3.0 的情況。
rickky at gmail dot com
17 年前
如果您正在寫入或儲存的目錄沒有設定正確的權限,您將不會收到任何錯誤訊息,而且看起來一切正常... 除了它沒有任何改變!

請務必收集 ZipArchive::close() 的返回值。如果它是 false... 表示它沒有成功。
Eric Langlois
10 年前
<?PHP
$zip
= new ZipArchive;
$res = $zip->open('test.zip', ZipArchive::CREATE);

if (
$res === TRUE) {
//程式碼寫在此處

$zip->close();
} else {
switch(
$res){
case
ZipArchive::ER_EXISTS:
$ErrMsg = "檔案已存在。";
break;

case
ZipArchive::ER_INCONS:
$ErrMsg = "Zip 檔案不一致。";
break;

case
ZipArchive::ER_MEMORY:
$ErrMsg = "記憶體配置失敗。";
break;

case
ZipArchive::ER_NOENT:
$ErrMsg = "找不到檔案。";
break;

case
ZipArchive::ER_NOZIP:
$ErrMsg = "不是 zip 檔案。";
break;

case
ZipArchive::ER_OPEN:
$ErrMsg = "無法開啟檔案。";
break;

case
ZipArchive::ER_READ:
$ErrMsg = "讀取錯誤。";
break;

case
ZipArchive::ER_SEEK:
$ErrMsg = "搜尋錯誤。";
break;

default:
$ErrMsg = "未知錯誤 (錯誤碼 $rOpen)";
break;


}
die(
'ZipArchive 錯誤:' . $ErrMsg);
}
?>
ohcc at 163 dot com
8 年前
ZipArchive::OVERWRITE 並不代表在呼叫 ZipArchive::open() 時會刪除現有的檔案。

事實上,現有檔案會在 PHP 將 zip 檔案儲存到磁碟之前被刪除。

PHP 採取以下步驟完成壓縮:

1. 呼叫 ZipArchive::open('xx.zip') 時
如果 'xx.zip' 存在且是一個 zip 檔案,它將會被開啟並作為一個暫存的 zip 檔案讀取;
如果檔案不存在,且套用了 ZipArchive::CREATE,PHP 將會建立一個空的暫存 zip 檔案。
在這些情況下,ZipArchive::open() 會回傳 true,否則會回傳一個整數錯誤碼。
2. 當呼叫 addFile()、addFromString() 等方法時,將檔案新增到暫存的 zip 檔案中。
3. 在將暫存的 zip 檔案儲存到磁碟之前,刪除現有的檔案。
4. 將暫存的 zip 檔案儲存到磁碟。
5. 當呼叫 ZipArchive::close() 或在腳本結束時,關閉作用中的檔案。

由於 PHP 在將 zip 檔案儲存到磁碟之前**不會**刪除現有的檔案,如果您想要壓縮該檔案所在的資料夾並將 zip 檔案儲存在該資料夾中,則應該使用 unset() 刪除它,否則每次重新整理頁面時,您都會得到一個越來越大 zip 檔案。
ohcc at 163 dot com
9 年前
ZipArchive::open() 的返回值及其數值和含義
ZipArchive::ER_SEEK 4 搜尋錯誤。
ZipArchive::ER_READ 5 讀取錯誤。
ZipArchive::ER_NOENT 9 找不到檔案。
ZipArchive::ER_OPEN 11 無法開啟檔案。
ZipArchive::ER_EXISTS 10 檔案已存在。
ZipArchive::ER_MEMORY 14 記憶體配置失敗。
ZipArchive::ER_INVAL 18 無效的參數。
ZipArchive::ER_NOZIP 19 不是 zip 檔案。
ZipArchive::ER_INCONS 21 zip 檔案不一致
laacz at laacz dot lv
3 年前
如果使用 PHP 8.0+ 版本,您可以使用 match 表達式來解碼狀態碼

<?php
$archive
= new \ZipArchive();
$result = $archive->open('some.file.zip');

$message = match ($result) {
\ZipArchive::ER_MULTIDISK => 'Multi-disk zip archives not supported',
\ZipArchive::ER_RENAME => 'Renaming temporary file failed',
\ZipArchive::ER_CLOSE => 'Closing zip archive failed',
\ZipArchive::ER_SEEK => 'Seek error',
\ZipArchive::ER_READ => 'Read error',
\ZipArchive::ER_WRITE => 'Write error',
\ZipArchive::ER_CRC => 'CRC error',
\ZipArchive::ER_ZIPCLOSED => 'Containing zip archive was closed',
\ZipArchive::ER_NOENT => 'No such file',
\ZipArchive::ER_EXISTS => 'File already exists',
\ZipArchive::ER_OPEN => 'Can\'t open file',
\ZipArchive::ER_TMPOPEN => 'Failure to create temporary file',
\ZipArchive::ER_ZLIB => 'Zlib error',
\ZipArchive::ER_MEMORY => 'Malloc failure',
\ZipArchive::ER_CHANGED => 'Entry has been changed',
\ZipArchive::ER_COMPNOTSUPP => 'Compression method not supported',
\ZipArchive::ER_EOF => 'Premature EOF',
\ZipArchive::ER_INVAL => 'Invalid argument',
\ZipArchive::ER_NOZIP => 'Not a zip archive',
\ZipArchive::ER_INTERNAL => 'Internal error',
\ZipArchive::ER_INCONS => 'Zip archive inconsistent',
\ZipArchive::ER_REMOVE => 'Can\'t remove file',
\ZipArchive::ER_DELETED => 'Entry has been deleted',
// \ZipArchive::ER_OK => 'No error',
default => 'No error',
};
?>
匿名
3 年前
要讀取 zip 檔案內容,可以使用 $zip->getNameIndex(i) 迴圈讀取 $zip->numFiles

例如:

$zipfilename = $dir . DIRECTORY_SEPARATOR . "test.zip";
$zippedFile = new ZipArchive;
$zippedFile->open($zipfilename);

echo "<LI>" . $zippedFile->getFromName("mpdf-8.0.7/.gitignore") . "</LI><HR>";

echo "<LI>已載入 $zipfilename " . $zippedFile->numFiles;
for ($i = 0; $i < $zippedFile->numFiles; $i++) {
$fn = $zippedFile->getNameIndex($i);
echo "<LI>$i: " . $fn;

if (strcmp(substr($fn, -1), DIRECTORY_SEPARATOR) == 0) echo "... 資料夾";
else echo "... " . strlen($zippedFile->getFromIndex($i)) . " 位元組";

}
}

$zippedFile->close();

YiiWanAB at hotmail dot com
13 年前
大多數情況下,人們使用 'opendir' 或 'readdir' 迭代目錄來將檔案添加到 zip 中。 像這樣...

while ($file = readdir($dir)) { ... $zip->addFile($file) }

請注意,$zip->addFile($file) 只有在您位於根目錄的當前目錄中才會有效。 您需要將正確的路徑添加到 $file 字串變數中以獲得完整的檔名,例如...

$zip->addFile($dir . DIRECTORY_SEPARATOR . $file); 這樣才會有效。

這可以幫助您找出關閉檔案時可能出現讀取錯誤的原因。

祝您使用愉快。
sebwoolford at gmail dot com
15 年前
關於 rickky at gmail dot com 所說的,我在嘗試快取 zip 檔案時也遇到了這個問題,我發現我必須將包含資料夾的權限設定為 777 才能使其正常工作。

因為如果在公開可檢視的資料夾中,這是一個潛在的安全漏洞,我建議移動該資料夾,使其不再位於 public_html 中。 然後,您可以使用 readfile() 將檔案輸出到瀏覽器,並使用一些 HTTP 標頭來告訴瀏覽器它是一個 zip 檔案。
Jonathan Baltazar
16 年前
如果使用 tempnam() 建立檔案,檔名不需要以 '.zip' 結尾。您只需要覆寫檔案,而不是嘗試讀取它

<?php
$file
= tempnam("tmp", "zip");

$zip = new ZipArchive();

// Zip 會開啟並覆寫檔案,而不是嘗試讀取它。
$zip->open($file, ZipArchive::OVERWRITE);

$zip->close();

// 將檔案串流傳輸給用戶端
header("Content-Type: application/zip");
header("Content-Length: " . filesize($file));
header("Content-Disposition: attachment; filename=\"a_zip_file.zip\"");
readfile($file);

unlink($file);
?>
michael202 at gmx dot de
15 年前
如果有人在
使用 `$zip->open($zipfile)` 開啟一個包含超過(大約)800 個檔案(即使它們位於子目錄中)的 ZIP 檔案時,遇到錯誤 ZIPARCHIVE::ER_READ = 5,

可能與錯誤 40873、8714 相關

1. 這不是作業系統的限制,因為在同一個位置對同一個檔案,從 Windows 透過 Samba 呼叫時可以正常運作

2. 在 32 位元 Linux 下無法運作

在 PHP 5.3.0 中,這個問題似乎已解決。
azsymvelik at gmail dot com
16 年前
<?php

/*
嗨!

我寫了一個檢查內容並「安全重新打包」的小腳本
*/

$somefile = "zip.zip";
$someurl = "/some/url";
$zip = new ZipArchive;
$open = $zip->open($somefile, ZIPARCHIVE::CHECKCONS);
// 如果壓縮檔損壞(或者只是將其他檔案重新命名為 *.zip),該函數將在 Windows 下的 httpd 上返回錯誤,因此最好使用 ZIPARCHIVE::CHECKCONS 檢查壓縮檔是否正常
if ($open === TRUE) {
if( !
$zip->extractTo($someurl)) {
die (
"解壓縮過程中發生錯誤");
}
$zip->close();
}
$new_archive_name = "new.zip";
$new_zip = new ZipArchive;
$new_open = $new_zip->open($new_archive_name, ZIPARCHIVE::CREATE);
if (
$new_open === TRUE) {
$dir = opendir($someurl);
while (
$file = readdir($dir)) {
if (
$file == "." || $file == "..") { } else {
//我們不希望這些檔案在新壓縮檔中
if ( !$new_zip->addFile($file)) {
print
$file."未被加入!<br />";
}
}
}
}
$new_zip->close();
?>
To Top