由於周圍沒有明顯的範例,這一點並不顯而易見,但您可以使用 $localname(第二個參數)來定義和控制 zip 內的檔案/目錄結構。如果您不希望檔案包含其絕對目錄樹,請使用它。
<?php
$zip->addFile($abs_path, $relative_path);
?>
(PHP 5 >= 5.2.0, PHP 7, PHP 8, PECL zip >= 1.1.0)
ZipArchive::addFile — 從指定路徑新增檔案到 ZIP 封存檔
$filepath
,$entryname
= "",$start
= 0,$length
= ZipArchive::LENGTH_TO_END,$flags
= ZipArchive::FL_OVERWRITE從指定路徑新增檔案到 ZIP 封存檔。
注意事項:為了最大的可攜性,建議在 ZIP 檔案名稱中一律使用正斜線 (
/
) 作為目錄分隔符號。
filepath
要新增的檔案路徑。
entryname
如果提供且不為空,則這是 ZIP 封存內部的本機名稱,將會覆寫 filepath
。
start
對於部分複製,這是起始位置。
length
對於部分複製,這是要複製的長度,如果為 ZipArchive::LENGTH_TO_END
(0),則使用檔案大小;如果為 ZipArchive::LENGTH_UNCHECKED
,則使用整個檔案(從 start
開始)。
flags
由 ZipArchive::FL_OVERWRITE
、ZipArchive::FL_ENC_GUESS
、ZipArchive::FL_ENC_UTF_8
、ZipArchive::FL_ENC_CP437
、ZipArchive::FL_OPEN_FILE_NOW
組成的位元遮罩。這些常數的行為在 ZIP 常數 頁面上有說明。
版本 | 說明 |
---|---|
8.0.0, PECL zip 1.18.0 |
新增了 flags 。 |
8.3.0, PECL zip 1.22.1 |
新增了 ZipArchive::FL_OPEN_FILE_NOW 。 |
8.3.0, PECL zip 1.22.2 |
新增了 ZipArchive::LENGTH_TO_END 和 ZipArchive::LENGTH_UNCHECKED 。 |
此範例開啟一個 ZIP 檔案 test.zip 並將檔案 /path/to/index.txt 新增為 newname.txt。
範例 #1 開啟並新增
<?php
$zip = new ZipArchive;
if ($zip->open('test.zip') === TRUE) {
$zip->addFile('/path/to/index.txt', 'newname.txt');
$zip->close();
echo 'ok';
} else {
echo 'failed';
}
?>
注意事項:
當設定要將檔案新增至壓縮檔時,PHP 會鎖定該檔案。只有在 ZipArchive 物件關閉後,鎖定才會釋放,關閉方式可以透過 ZipArchive::close() 或 ZipArchive 物件被銷毀。這可能會導致您無法刪除正在新增的檔案,直到鎖定被釋放為止。
由於周圍沒有明顯的範例,這一點並不顯而易見,但您可以使用 $localname(第二個參數)來定義和控制 zip 內的檔案/目錄結構。如果您不希望檔案包含其絕對目錄樹,請使用它。
<?php
$zip->addFile($abs_path, $relative_path);
?>
注意:在不存在的檔案上呼叫 $zip->addFile() 會成功並返回 TRUE,將失敗延遲到您進行最終的 $zip->close() 呼叫,這將返回 FALSE 並可能讓您摸不著頭緒。
如果您要將多個檔案添加到 zip 檔中,並且您的 $zip->close() 呼叫返回 FALSE,請確保您添加的所有檔案確實存在。
在呼叫 $zip->addFile() 之前,使用 file_exists() 或 is_readable() 檢查每個檔案也是一個好主意。
將檔案添加到 zip 檔時,檔案會被開啟並保持開啟狀態。
當添加超過 1024 個檔案時(取決於您的開啟檔案限制),伺服器會停止添加檔案,導致您的 zip 封存檔狀態為 11。使用 addFiles 超過此開啟檔案限制時沒有警告。
使用 ulimit -a 檢查您開啟的檔案。
這讓我忙了一陣子。
說明文件有誤。
「簡而言之,這表示您可以在封存檔關閉後先刪除已添加的檔案。」
這是真的,但不是透過鎖定檔案...
警告!此方法是非同步運作的!
addFile() 似乎會在檔案 stat 命令正確返回時返回 TRUE,但操作本身不會立即發生。
相反,刪除檔案始終是可能的。我透過使用一個臨時檔案並在 addFile() 返回後立即將其刪除發現了這種行為。結果是沒有建立封存檔,也沒有添加任何檔案,儘管之前的每個操作(建立、open()、addFile())都返回 true。操作靜默失敗。
如果您在 Windows 檔案總管讀取由 Linux 建立的 zip 檔案時遇到問題,請嘗試
$oZip->addFile ( $file_name, " " . basename ( $file_name ) )
那個空格「 」應該可以解決問題。
有一些地方可以更清楚一些,我花了一些時間才弄明白。希望它能幫助你。
1.使用 addFile() 方法將帶有路徑的檔案添加到 zip 檔中。如果目錄不存在,addFile() 會自動建立它。
<?php
....
//addFile 會在新增 filename.txt 檔案之前,先建立名為 not_exist_director 的目錄。
$zip->addFile($fileToAdd, '/not_exist_directory/filename.txt');
...
?>
2. 預設情況下,addFile() 會覆寫舊檔案(如果存在)。
<?php
....
//如果 filename.txt 已經存在於 zip 檔案中,addFile() 會將其覆寫。因為 addFile 的第五個參數預設為 ZipArchive::FL_OVERWRITE。
$zip->addFile($fileToAdd, '/filename.txt');
...
?>
兩個小技巧
- 本頁的範例對於新手程式設計師來說有點誤導。它只有在 ZIP 歸檔檔案已經存在的情況下才能正常運作。別忘了在 open() 函式的第二個選用參數中使用 ZipArchive::CREATE,以及選擇性地使用 ZipArchive::OVERWRITE。
- 如果你想要遞迴地新增檔案和目錄(請參閱此頁面上其他評論中的一些範例),請使用 scandir() 而不是 blob(),因為 blob() 不會列出隱藏檔案,例如「.htaccess」。
如果壓縮大型檔案,上述的解決方法 (file_get_contents) 非常危險。(請參閱記憶體限制)。
請定期關閉/開啟 zip 歸檔,而不是使用 file_get_contents()。
如果您新增具有絕對路徑的檔案,例如
/mnt/repository/my_file.pdf
標準的 Windows zip 工具將無法解壓縮這些檔案。第一個斜線會導致 zip 工具出錯。您必須新增相對檔案路徑或使用符號連結。
對我來說有效
$zip = new ZipArchive;
$zip_name = ('name.zip');
$path_zip = ($config['path'].'/zip/'.$zip_name);
$zip->open($path_zip,ZipArchive::CREATE);
$zip->addFile($path1.'/'.$nam1,$nam1);
$zip->addFile($path2.'/'.$nam2,$nam2);
$zip->close();
再見
GioMBG
在我的系統(Windows)上,我發現 ZipArchive 使用 IBM850 編碼檔名 (localname)。對於包含特殊字元(例如 (é) é)的檔名,它在 ISO-8859-1 中顯示為 0xE9,在 IBM850 中顯示為 0x82。我必須呼叫 iconv('ISO-8859-1', 'IBM850', 'Québec') 才能獲得正確的檔名。
請注意,使用 addFile() 會更改 zip 檔案內檔案的順序,實際上是更改 zip 索引中的順序。影響不大,除非您迴圈索引並在該迴圈內使用 addFile():它可能會產生混亂的結果。
範例
<?php
$zip = new ZipArchive;
if ($zip->open('somefile.zip') === TRUE) {
for ($i = 0; $i < $zip->numFiles; $i++) {
if (forsomereason()) {
addFile('./somenewfile.ext', $zip->getNameIndex($i));
}
}
}
$zip->close();
?>
這段程式碼可能會永遠循環執行,取決於你的 forsomereason() 函式,或者至少你存在這個風險。
可以試試這樣寫:
<?php
$zip = new ZipArchive;
if ($zip->open('somefile.zip') === TRUE) {
for ($i = 0; $i < $zip->numFiles; $i++) {
if (forsomereason()) {
$couples[]=array('filename'=>'./somenewfile.ext','localname'=>$zip->getNameIndex($i));
}
}
}
foreach ($couples as $couple) $zip->addFile($couple['filename'],$couple['localname']);
$zip->close();
?>
希望對您有所幫助 ;-)
我在 Linux 網頁伺服器上需要壓縮大量的檔案和資料夾。我遇到了逾時問題、檔案列舉問題,以及檔案控制代碼限制問題 (ulimit)。我先使用了 Farzad Ghanei 提供的用於解決 ulimit 問題的腳本 (ZipArchiveImproved),但以他的方式關閉並重新開啟檔案並沒有解決我的問題。
我最終對我創建的 $filelimit 變數進行了一個簡單的呼叫,該變數記錄了我希望腳本在關閉並重新開啟檔案之前達到的檔案控制代碼限制。
<?php
$filelimit = 255;
if ($zip->numFiles == $filelimit) {$zip->close(); $zip->open($file) or die ("Error: Could not reopen Zip");}
?>
這樣我有一些進展,逾時消失了,但是當在重新開啟 Zip 後呼叫
<?php $zip->addFile($filepath, $archivefilepath); ?>
時,我收到一個錯誤。我印出 <?php $zip->numFiles; ?> 並發現重新開啟後,numFile 枚舉值重置為 '0'。
經過一番嘗試之後,我嘗試使用 addFromString 並得到了一些更好的結果,但在實際上將 addFromString 與 addFile 結合之前,我並沒有讓它 100% 正常工作!我用於在大型檔案資料夾結構上新增檔案的有效腳本如下所示
<?php
$sourcefolder = /rel/path/to/source/folder/on/server/
$dirlist = new RecursiveDirectoryIterator($sourcefolder);
$filelist = new RecursiveIteratorIterator($dirlist);
//how many file can be added before a reopen is forced?
$filelimit = 245;
// Defines the action
$file = tempnam("tmp", "zip");
$zip = new ZipArchive();
// This creates and then gives the option to save the zip file
if ($zip->open($file, ZipArchive::OVERWRITE) !== TRUE) {
die ("Could not open archive");
}
// adds files to the file list
foreach ($filelist as $key=>$value) {
//fix archive paths
$path = str_replace($sourcefolder, "", $key); //remove the source path from the $key to return only the file-folder structure from the root of the source folder
if (!file_exists($key)) { die($key.' does not exist. Please contact your administrator or try again later.'); }
if (!is_readable($key)) { die($key.' not readable. Please contact your administrator or try again later.'); }
if ($zip->numFiles == $filelimit) {$zip->close(); $zip->open($file) or die ("Error: Could not reopen Zip");}
$zip->addFromString($path, $key) or die ("ERROR: Could not add file: $key </br> numFile:".$zip->numFiles);
$zip->addFile(realpath($key), $path) or die ("ERROR: Could not add file: $key </br> numFile:".$zip->numFiles);
}
// closes the archive
$zip->close();
//make local temp file a .zip, rename, and move to output dir
rename ($file, "./" . $outputfolder . "/" . $zipfilename);
?>
I hope this may help someone else.
這是 ZipArchive 的一個小擴展,可以遞迴處理目錄
<?php
class Zipper extends ZipArchive {
public function addDir($path) {
print '新增 ' . $path . '<br>';
$this->addEmptyDir($path);
$nodes = glob($path . '/*');
foreach ($nodes as $node) {
print $node . '<br>';
if (is_dir($node)) {
$this->addDir($node);
} else if (is_file($node)) {
$this->addFile($node);
}
}
}
} // Zipper 類別
?>
不要使用 ZipArchive::addFile() 來添加資料夾。
當資料夾的路徑傳遞給 ZipArchive::addFile() 時,該方法會返回 true,但 ZipArchive 無法建立 zip 封存檔,也無法對現有檔案進行任何更改。
<?php
$z = new ZipArchive();
if(true === ($z->open('./foo.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE))){
$z->setArchiveComment('Interesting!');
$z->addFromString('domain.txt', 'wuxiancheng.cn');
$folder = './test';
!is_dir($folder) && mkdir($folder); // 建立一個測試用的資料夾
if(true === $z->addFile($folder)){
echo 'success'; // !!!
}
rmdir($folder);
$z->close();
// foo.zip 並不會儲存到磁碟上。
// 如果 foo.zip 在執行此腳本之前已存在,則檔案將保持不變。
}
?>
正如其他人所指出的,ZipArchive::addFile() 是非同步的。如果您想限制新增大量檔案的時間,這就是方法。
<?php
// $files 是一個包含大量檔名的陣列。
// $fileCount 是 $files 中的檔案總數,例如 count($files)
// $fileIndex 是您想要開始處理的檔案索引
for(
$fileIndex = 1, $start = hrtime( true );
$fileIndex < $fileCount && $secondsElapsed < 5.0;
$fileIndex++
) {
$currentfile = $files[ $fileIndex ];
if( file_exists( $currentfile ) ) {
$zip->addFile( $currentfile );
usleep(1);
$secondsElapsed = ( hrtime( true ) - $start ) / 1e+9;
}
}
?>
這會持續添加檔案,直到達到逾時時間 (5.0 秒)。如果沒有 usleep(1),它會一直持續到達到 max_execution_time 為止。
zip->addFile 函式並不會立即將檔案添加到壓縮檔中。它會被排入佇列,並在 zip->close() 時執行。
我在一個目錄中有備份檔案。我使用 foreach 迴圈將檔案添加到壓縮檔,然後立即將檔案移至 dump 目錄。
這會導致錯誤。
<?php
$dir = getcwd().'/';
$dirContent = scandir('./');
foreach($dirContent as $file)
if(strpos($file, '.sql')!==false)
{
echo "正在新增 $file ......";
$zip->addFile($file);
$newFileName = $dir."done/$file";
rename($dir.$file, $newFileName);
echo( "; 檔案已新增\n");
}
$zip->close();
?>
在移動檔案之前,需要先執行 $zip->close()。
<?php
foreach($dirContent as $file)
if(strpos($file, '.sql')!==false)
{
echo "正在新增 $file ......";
$zip->addFile($file);
echo( "; 檔案已新增\n");
}
$zip->close();
foreach($dirContent as $file)
if(strpos($file, '.sql')!==false)
{
$newFileName = $dir."done/$file";
rename($dir.$file, $newFileName);
echo( "$file 已移動\n");
}
?>
如果您在上次 Ubuntu 更新後使用 ZipArchiveImproved 時遇到一些警告,請在 reopen 函式中將 "self::CREATE" 替換為 "self::CREATE | self::OVERWRITE"。
christophe
這是一個繼承 ZipArchive 的基本類別,用於
* 新增回報 ZIP 檔案位址的功能(我需要它,但在 ZipArchive 文件中找不到方法)。
* 解決因檔案描述符限制而向保存檔新增過多檔案的問題。ZipArchiveImproved::addFile() 會處理這個問題。
<?php
/**
* ZipArchiveImproved extends ZipArchive to add some information about the zip file and some functionality.
*
*
*
* @author Farzad Ghanei
* @uses ZipArchive
* @version 1.0.0 2009-01-18
*/
class ZipArchiveImproved extends ZipArchive {
protected $_archiveFileName = null;
protected $_newAddedFilesCounter = 0;
protected $_newAddedFilesSize = 100;
/**
* returns the name of the archive file.
*
* @return string
*/
public function getArchiveFileName() {
return $this->_archiveFileName;
}
/**
* returns the number of files that are going to be added to ZIP
* without reopenning the stream to file.
*
* @return int
*/
public function getNewAddedFilesSize() {
return $this->_newAddedFilesSize;
}
/**
* sets the number of files that are going to be added to ZIP
* without reopenning the stream to file. if no size is specified, default is 100.
*
* @param int
* @return ZipArchiveImproved self reference
*/
public function setNewlAddedFilesSize($size=100) {
if ( empty($size) || !is_int($size) || $size < 1) {
$size = 100;
}
$this->_newAddedFilesSize = $size;
return $this;
}
/**
* opens a stream to a ZIP archive file. calls the ZipArchive::open() internally.
* overwrites ZipArchive::open() to add the archiveFileName functionality.
*
* @param string $fileName
* @param int $flags
* return mixed
*/
public function open($fileName, $flags) {
$this->_archiveFileName = $fileName;
$this->_newAddedFilesCounter = 0;
return parent::open($fileName,$flags);
}
/**
* closes the stream to ZIP archive file. calls the ZipArchive::close() internally.
* overwrites ZipArchive::close() to add the archiveFileName functionality.
*
* @return bool
*/
public function close() {
$this->_archiveFileName = null;
$this->_newAddedFilesCounter = 0;
return parent::close();
}
/**
* closes the connection to ZIP file and openes the connection again.
*
* @return bool
*/
public function reopen() {
$archiveFileName = $this->_archiveFileName;
if ( !$this->close() ) {
return false;
}
return $this->open($archiveFileName,self::CREATE);
}
/**
* adds a file to a ZIP archive from the given path. calls the ZipArchive::addFile() internally.
* overwrites ZipArchive::addFile() to handle maximum file connections in operating systems.
*
* @param string $fileName the path to file to be added to archive
* @param string [optional] $localname the name of the file in the ZIP archive
* @return bool
*/
public function addFile( $fileName ) {
if ($this->_newAddedFilesCounter >= $this->_newAddedFilesSize) {
$this->reopen();
}
if ( func_num_args() > 1 ) {
$flags = func_get_arg(1);
$added = parent::addFile($fileName,$flags);
if ($added) {
$this->_newAddedFilesCounter++;
}
return $added;
}
$added = parent::addFile($fileName);
if ($added) {
$this->_newAddedFilesCounter++;
}
return $added;
} // public function addFile()
}
?>
addFile() 方法不接受 "file://" 協定。請移除 "file://" 並使用絕對路徑。
我假設它也不接受任何其他協定,我在嘗試新增檔案時遇到問題,我正在建構的應用程式中的其他函式需要使用協定。
另外需要注意的是,返回的狀態碼與任何預定義的錯誤碼都不匹配,而且狀態碼從來都不相同。我猜測是變數溢位,因為狀態碼大約在最小和最大 INT 值附近。
請注意,ZIP 檔案沒有「資料夾」的概念。如果您需要將資料儲存到資料夾中,請在 $localname 中使用正斜線 ("/") 來分隔資料夾和檔案名稱。
範例
$zip->addFile("test.txt", "mainfolder/subfolder/test.txt");
這個新增目錄函式不需要您建立新的包裝類別,也不會將整個檔案目錄樹新增到您的 zip 檔案中。
<?php
public function addDirectoryToZip($zip, $dir, $base)
{
$newFolder = str_replace($base, '', $dir);
$zip->addEmptyDir($newFolder);
foreach(glob($dir . '/*') as $file)
{
if(is_dir($file))
{
$zip = $this->addDirectoryToZip($zip, $file, $base);
}
else
{
$newFile = str_replace($base, '', $file);
$zip->addFile($file, $newFile);
}
}
return $zip;
}
?>
或許這會派上用場
遞迴式地將目錄中的所有目錄和檔案加入
class zip extends ZipArchive {
public function addDirectory($dir) { // 加入目錄
foreach(glob($dir . '/*') as $file) {
if(is_dir($file))
$this->addDirectory($file);
else
$this->addFile($file);
}
}
}
目前使用 addFile 將檔案新增至 ZIP 封存檔的數量(直到關閉)會受限於檔案描述符的限制。這是一個簡單的解決方法(在下面的錯誤連結中,您可以找到其他解決方法)
<?php
/** 解決檔案描述符數量限制的因應措施(避免在 ZIP 檔案中新增超過通常 253 或 1024 個檔案時發生錯誤) */
function addFileToZip( $zip, $path, $zipEntryName ) {
// 使用 $zip->addFile() 會在新增一定數量檔案後失敗,並回傳狀態 ZIPARCHIVE::ER_OPEN
// 因為 ZipArchive 內部會儲存所有已新增檔案的檔案描述符,
// 且僅在關閉時才會將內容寫入 ZIP 檔案
// 參考:http://bugs.php.net/bug.php?id=40494
// 以及:http://pecl.php.net/bugs/bug.php?id=9443
// return $zip->addFile( $path, $zipEntryName );
$contents = file_get_contents( $path );
if ( $contents === false ) {
return false;
}
return $zip->addFromString( $zipEntryName, $contents );
}
?>
另一個令人頭痛的問題。為了在將檔案新增到壓縮檔後可以安全地刪除它,我利用了以下的結構:
if (!is_file ($archive))
$result = $zip->open ($archive, ZipArchive::CREATE);
else
$result = $zip->open ($archive);
endif;
if ($result === TRUE)
if (($zip->addFile ($file, $filename)) === TRUE)
$theoreticaly_added = TRUE;
endif;
if ((($zip->close ()) === TRUE) && $theoreticaly_added)
unlink ($file);
endif;
endif;
現在檔案不會消失了。但也許有更簡單的解決方案,可以提供 100% 的保證?
我嘗試新增檔案時遇到幾個問題,是因為路徑問題。出現的錯誤如下:
ZipArchive::addFile() [function.ZipArchive-addFile]: 無法存取 <path>
我使用了以 "/" 開頭的絕對根路徑,但它無效。請嘗試以 "./" 開始您的路徑(參考您網站的根目錄)。
如果檔案名稱包含中文字,`ZipArchive::addFile()` 會導致無法將檔案壓縮到壓縮包中,或是壓縮後檔案名稱亂碼。
可以使用 `ZipArchive::addFromString()` 來解決。
注意:如果作業系統是 Windows,檔案系統編碼是 gbk。
如果 PHP 檔案的編碼是 UTF-8,則需要進行相應的轉碼。
如果檔案名稱包含中文字,`ZipArchive::addFile()` 會失敗。
應該使用 `ZipArchive::addFromString()` 來代替。
<?php
$z = new ZipArchive;
$file = '吳先成.txt';
if($z->open(ZIPARCHIVE::CREATE)===true){
$z->addFromString($file, file_get_contents($file));
//適用於 Windows 系統
//$z->addFromString($file, file_get_contents(iconv('utf-8', 'gbk//ignore', $file)));
}
// ...
需要注意的是,上面提供的範例並不準確。
與 extractTo 不同,zip_open 並不會回傳布林值結果,因此上述範例永遠會失敗。
請注意,ZipArchive::open() 回傳的不是布林值,而是整數,例如:
$zip = new ZipArchive();
$filename = '/tmp/test.zip';
var_dum($zip->open($filename)); // 回傳 : int (11)
需要注意的是,在呼叫 $zip->close() 方法之前,檔案實際上並未加入到壓縮檔中。我花了很多時間試圖找出為什麼透過 $zip->addFile() 加入大型檔案後沒有經過任何 time(),但接著卻讓腳本逾時。
<?php
$zip=new ZipArchive;
$zip->addFile('檔案的路徑', '檔案的新名稱');
?>
如果我希望壓縮檔中的檔案名稱與壓縮前的名稱相同,我該怎麼做?
(抱歉,我來自捷克共和國,我的英文不太好,如果我有任何錯誤,請忽略它 :-))
這是我的解決方法,透過定期關閉/開啟壓縮檔來處理檔案描述符限制。
<?php
if($backup = new ZipArchive()) {
if($backup->open($zip, ZIPARCHIVE::OVERWRITE) === true) {
$backup->addFile($file['realpath'], $file['path']);
if(($count++) == 200) { // 檔案描述符限制
$backup->close();
if($backup = new ZipArchive()) {
$backup->open($zip);
$count = 0;
}
}
}
$backup->close();
}
?>
希望這對某些人有所幫助。