这并不明显,因为周围没有明显的例子,但你可以使用 $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 / 1.18.0 |
添加了 flags 。 |
8.3.0 / 1.22.1 |
添加了 ZipArchive::FL_OPEN_FILE_NOW 。 |
8.3.0 / 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 会帮助你创建名为 not_exist_director 的目录,然后再添加 filename.txt 文件。
$zip->addFile($fileToAdd, '/not_exist_directory/filename.txt');
...
?>
2.addFile() 会默认覆盖旧文件(如果存在)。
<?php
....
//如果 filename.txt 存在于 zip 中,addFile() 会覆盖它。因为 addFile 的第五个参数默认情况下为 ZipArchive::FL_OVERWRITE。
$zip->addFile($fileToAdd, '/filename.txt');
...
?>
2 个提示
- 此页面上的示例对新手程序员来说有点误导。 它只在 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();
ciao
GioMBG
在我的系统(Windows)上,我发现 ZipArchive 使用 IBM850 编码作为文件名(本地名)。 对于包含特殊字符的文件名,例如 (é) é,它在 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 提供的脚本(ZipArchiveImproved)来解决 u limit 问题,但以他的方式关闭和重新打开并没有解决我的问题。
我最终创建了一个名为 $filelimit 的简单变量,它记录我想要脚本在关闭和重新打开文件之前达到的文件句柄限制。
<?php
$filelimit = 255;
if ($zip->numFiles == $filelimit) {$zip->close(); $zip->open($file) or die ("Error: Could not reopen Zip");}
?>
这让我取得了一些进展,超时问题消失了,但是当我调用
<?php $zip->addFile($filepath, $archivefilepath); ?>
重新打开 Zip 之后,我遇到了错误。 我回显了 <?php $zip->numFiles; ?>,发现重新打开后,numFile 枚举重置为 '0'。
经过更多尝试后,我尝试使用 addFromString,结果好了一些,但直到我真正将 addFromString 与 addFile 结合使用才使其完全正常工作! 我用于处理大量文件和文件夹结构的添加文件函数的脚本如下
<?php
$sourcefolder = /rel/path/to/source/folder/on/server/
$dirlist = new RecursiveDirectoryIterator($sourcefolder);
$filelist = new RecursiveIteratorIterator($dirlist);
// 在强制重新打开之前可以添加多少个文件?
$filelimit = 245;
// 定义操作
$file = tempnam("tmp", "zip");
$zip = new ZipArchive();
// 这将创建并提供保存 zip 文件的选项
if ($zip->open($file, ZipArchive::OVERWRITE) !== TRUE) {
die ("Could not open archive");
}
// 将文件添加到文件列表
foreach ($filelist as $key=>$value) {
// 修复存档路径
$path = str_replace($sourcefolder, "", $key); // 从 $key 中删除源路径,以仅返回从源文件夹根目录开始的文件和文件夹结构
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);
}
// 关闭存档
$zip->close();
// 将本地临时文件设为 .zip,重命名并移动到输出目录
rename ($file, "./" . $outputfolder . "/" . $zipfilename);
?>
希望这能帮助其他人。
这是 ZipArchive 的一个小扩展,可以递归地处理目录
<?php
class Zipper extends ZipArchive {
public function addDir($path) {
print 'adding ' . $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);
}
}
}
} // class 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 and $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_excecution_time 为止。
zip->addfile 函数不会立即将文件添加到存档。它是排队的,并在 zip->close() 时执行。
我有一个目录中的备份文件,使用 foreach 语句,我将文件添加到存档,然后立即将文件移动到转储目录。
这会导致错误。
<?php
$dir = getcwd().'/';
$dirContent = scandir('./');
foreach($dirContent as $file)
if(strpos($file, '.sql')!==false)
{
echo "adding $file ......";
$zip->addFile($file);
$newFileName = $dir."done/$file";
rename($dir.$file, $newFileName);
echo( "; file added\n");
}
$zip->close();
?>
需要在移动文件之前执行 $zip->close()。
<?php
foreach($dirContent as $file)
if(strpos($file, '.sql')!==false)
{
echo "adding $file ......";
$zip->addFile($file);
echo( "; file added\n");
}
$zip->close();
foreach($dirContent as $file)
if(strpos($file, '.sql')!==false)
{
$newFileName = $dir."done/$file";
rename($dir.$file, $newFileName);
echo( "$file moved\n");
}
?>
如果你在最近的 Ubuntu 更新后遇到了一些与 ZipArchiveImproved 相关的警告,请在 reopen 函数中将 "self::CREATE" 替换为 "self::CREATE | self::OVERWRITE"。
christophe
这是一个扩展 ZipArchive 的基本类,它:
* 添加了一个功能来报告 ZIP 文件地址(我需要它,但在 ZipArchive 文档中找不到如何实现)。
* 解决由于文件描述符限制而导致向存档添加太多文件的问题。ZipArchiveImproved::addFile() 处理了这个问题。
<?php
/**
* ZipArchiveImproved 扩展了 ZipArchive,添加了一些关于 zip 文件的信息和一些功能。
*
*
*
* @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;
/**
* 返回存档文件的名称。
*
* @return string
*/
public function getArchiveFileName() {
return $this->_archiveFileName;
}
/**
* 返回将要添加到 ZIP 中的文件数量
* 而不重新打开文件流。
*
* @return int
*/
public function getNewAddedFilesSize() {
return $this->_newAddedFilesSize;
}
/**
* 设置将要添加到 ZIP 中的文件数量
* 而不重新打开文件流。 如果没有指定大小,则默认值为 100。
*
* @param int
* @return ZipArchiveImproved 自引用
*/
public function setNewlAddedFilesSize($size=100) {
if ( empty($size) || !is_int($size) || $size < 1) {
$size = 100;
}
$this->_newAddedFilesSize = $size;
return $this;
}
/**
* 打开到 ZIP 存档文件的流。 在内部调用 ZipArchive::open()。
* 覆盖 ZipArchive::open() 以添加 archiveFileName 功能。
*
* @param string $fileName
* @param int $flags
* return mixed
*/
public function open($fileName, $flags) {
$this->_archiveFileName = $fileName;
$this->_newAddedFilesCounter = 0;
return parent::open($fileName,$flags);
}
/**
* 关闭到 ZIP 存档文件的流。 在内部调用 ZipArchive::close()。
* 覆盖 ZipArchive::close() 以添加 archiveFileName 功能。
*
* @return bool
*/
public function close() {
$this->_archiveFileName = null;
$this->_newAddedFilesCounter = 0;
return parent::close();
}
/**
* 关闭与 ZIP 文件的连接,然后重新打开连接。
*
* @return bool
*/
public function reopen() {
$archiveFileName = $this->_archiveFileName;
if ( !$this->close() ) {
return false;
}
return $this->open($archiveFileName,self::CREATE);
}
/**
* 从给定路径将文件添加到 ZIP 存档。 在内部调用 ZipArchive::addFile()。
* 覆盖 ZipArchive::addFile() 以处理操作系统中的最大文件连接。
*
* @param string $fileName 要添加到存档的文件路径
* @param string [可选] $localname 存档中文件的名称
* @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
/** 解决文件描述符数量限制问题(避免在添加超过 253 或 1024 个文件时失败
* 到 ZIP) */
function addFileToZip( $zip, $path, $zipEntryName ) {
// 这将导致状态 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]: Unable to access <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));
//for windows
//$z->addFromString($file, file_get_contents(iconv('utf-8', 'gbk//ignore', $file)));
}
// ...
注意,ZipArchive::open()不返回布尔值,而是整数,例如
$zip = new ZipArchive();
$filename = '/tmp/test.zip';
var_dum($zip->open($filename)); // 返回: int (11)
注意,直到调用$zip->close()方法,文件才会真正添加到存档中。我花了很长时间试图弄清楚为什么在通过$zip->addFile()添加大型文件后,没有时间过去了,但随后会超时脚本。
<?php
$zip=new ZipArchive;
$zip->addFile('path to the file', 'new name of the file');
?>
当我想要在压缩存档中使用它之前的文件名时,我该怎么做?
(对不起,我来自捷克共和国,英语不太好,如果我在任何地方犯了错误,请忽略它 :-)
这是我通过定期关闭/打开存档文件来解决文件描述符限制的变通方法。
<?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();
}
?>
希望它能帮助到一些人。