PHP Conference Japan 2024

ZipArchive::addFile

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

ZipArchive::addFile从给定路径将文件添加到 ZIP 归档

描述

public ZipArchive::addFile(
    string $filepath,
    string $entryname = "",
    int $start = 0,
    int $length = ZipArchive::LENGTH_TO_END,
    int $flags = ZipArchive::FL_OVERWRITE
): bool

从给定路径将文件添加到 ZIP 归档。

注意: 为了最大限度地提高可移植性,建议始终使用正斜杠 (/) 作为 ZIP 文件名中的目录分隔符。

参数

filepath

要添加的文件的路径。

entryname

如果提供且不为空,则这是 ZIP 归档内的本地名称,它将覆盖 filepath

start

对于部分复制,起始位置。

length

对于部分复制,要复制的长度,如果为 ZipArchive::LENGTH_TO_END (0),则使用文件大小;如果为 ZipArchive::LENGTH_UNCHECKED,则使用整个文件(从 start 开始)。

flags

ZipArchive::FL_OVERWRITEZipArchive::FL_ENC_GUESSZipArchive::FL_ENC_UTF_8ZipArchive::FL_ENC_CP437ZipArchive::FL_OPEN_FILE_NOW 组成的位掩码。这些常量的行为在 ZIP 常量 页面中有描述。

返回值

成功时返回 true,失败时返回 false

变更日志

版本 描述
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_ENDZipArchive::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 对象被销毁后,才会释放该锁定。这可能会阻止您在释放锁定之前删除正在添加的文件。

参见

添加备注

用户贡献的笔记 34 条笔记

jayarjo
14 年前
由于周围没有明显的示例,因此这一点并不明显,但是您可以使用 $localname(第二个参数)来定义和控制 zip 内的文件/目录结构。如果您不希望文件包含其绝对目录树,请使用它。

<?php

$zip
->addFile($abs_path, $relative_path);

?>
john factorial
13 年前
注意:对不存在的文件调用 $zip->addFile() 将成功并返回 TRUE,直到您进行最终的 $zip->close() 调用才会延迟失败,这将返回 FALSE 并可能让您不知所措。

如果您向 zip 中添加多个文件,并且您的 $zip->close() 调用返回 FALSE,请确保您添加的所有文件都确实存在。

在对文件调用 $zip->addFile() 之前,最好也使用 file_exists() 或 is_readable() 检查每个文件。
aartdebruijn at gmail dot com
14 年前
将文件添加到 zip 时,文件将被打开并保持打开状态。
添加超过 1024 个文件时(取决于您的打开文件限制),服务器将停止添加文件,从而导致您的 zip 归档中出现状态 11。使用 addFiles 超过此打开文件限制时没有警告。

使用 ulimit -a 检查您的打开文件。

这让我忙了好一阵子。
frame86 at live dot com
10 年前
手册是错的。

“简而言之,这意味着您可以在存档关闭后首先删除已添加的文件。”

这是真的,但不是通过锁定文件……
警告!此方法异步工作!

似乎如果文件 stat 命令返回正确,则 addFile() 将返回 TRUE,但操作本身尚未发生。

相反,始终可以删除文件。我通过使用临时文件并在 addFile() 返回后立即删除它来发现此行为。结果是没有创建任何存档,也没有添加任何文件,尽管在之前每个操作(创建、open()、addFile())都返回 true。操作会静默失败。
romuloum at hotmail dot com
10 年前
如果您在使用 linux 创建的 zipfile 时遇到 windows 资源管理器读取问题,请尝试
$oZip->addFile ( $file_name, " " . basename ( $file_name ) )
那个空格 " " 应该可以解决。
610010559 at qq dot com
2 年前
有些点可以更清楚一些,我花了一些时间才弄清楚。希望它能帮到你。
1. 使用 addFile() 方法将带路径的文件添加到 zip。如果目录不存在,addFile() 将自动创建它。
<?php
....
// addFile 将帮助您在添加 filename.txt 文件之前创建名为 not_exist_director 的目录。
$zip->addFile($fileToAdd, '/not_exist_directory/filename.txt');
...
?>

2. addFile() 将默认情况下覆盖现有文件。
<?php
....
//如果zip文件中已存在filename.txt,addFile() 会覆盖它。因为addFile的第五个参数默认是ZipArchive::FL_OVERWRITE。
$zip->addFile($fileToAdd, '/filename.txt');
...
?>
stanislav dot eckert at vizson dot de
9年前
2条建议

- 此页面上的示例对于新手程序员来说有点误导性。它只有在ZIP归档文件已存在的情况下才能工作。不要忘记在open()函数的第二个可选参数中使用ZipArchive::CREATE和可选的ZipArchive::OVERWRITE。

- 如果你想递归地添加文件和目录(参见此页面上其他评论中的一些示例),请使用scandir()而不是blob(),因为blob()不会列出隐藏文件,例如“.htaccess”。
camuc at camuc dot net
14 年前
在这个库的某些版本中,你需要添加“localfile”参数,否则文件不会显示在Zip文件夹中。
wacher at freemail dot hu
16年前
上面的解决方法(file_get_contents)在打包大型文件时非常危险。(参见内存限制)。
定期关闭/打开zip存档,而不是使用file_get_contents()。
garcia at no_span dot krautzer-lynn dot com
15年前
如果你添加具有绝对路径的文件,例如
/mnt/repository/my_file.pdf
标准的Windows zip实用程序将无法提取这些文件。第一个斜杠会使zip实用程序出错。你必须添加相对文件路径或使用符号链接。
gio AT giombg dot com
6年前
对我有用

$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
匿名用户
14 年前
在我的系统(Windows)上,我发现ZipArchive使用IBM850编码作为文件名(localname)。对于包含特殊字符的文件名,例如(é) &eacute;(在ISO-8859-1中为0xE9),在IBM850中为0x82。我必须调用iconv('ISO-8859-1', 'IBM850', 'Québec')才能获得正确的文件名。
sp at read dot eu
12年前
请注意,使用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();
?>

希望对您有所帮助 ;-)
kris at blacksuitmedia [do/t/] c0m
11年前
我需要在一个Linux Web服务器上压缩大量的文件和文件夹。我遇到了超时问题和文件枚举器问题,以及文件句柄限制问题(ulimit)。我使用Farzad Ghanei首先提供的脚本(ZipArchiveImproved)来解决ulimit问题,但是用他的方法关闭并重新打开并没有解决我的问题。

我最终对一个我创建的`$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结合使用后,才使其100%工作。我在大型文件文件夹结构上用于添加文件的可运行脚本如下所示

<?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 (
"无法打开压缩文件");

}

// 将文件添加到文件列表
foreach ($filelist as $key=>$value) {

//修复存档路径
$path = str_replace($sourcefolder, "", $key); //从$key中移除源路径,只返回从源文件夹根目录开始的文件-文件夹结构
if (!file_exists($key)) { die($key.' 不存在。请联系管理员或稍后再试。'); }
if (!
is_readable($key)) { die($key.' 不可读。请联系管理员或稍后再试。'); }
if (
$zip->numFiles == $filelimit) {$zip->close(); $zip->open($file) or die ("错误:无法重新打开 Zip 文件");}

$zip->addFromString($path, $key) or die ("错误:无法添加文件: "$key "</br> numFile:".$zip->numFiles);
$zip->addFile(realpath($key), $path) or die ("错误:无法添加文件: "$key "</br> numFile:".$zip->numFiles);

}

// 关闭压缩文件
$zip->close();

//将本地临时文件改为.zip,重命名并移动到输出目录
rename ($file, "./" . $outputfolder . "/" . $zipfilename);
?>
我希望这能帮助其他人。
peter at boring dot ch
15年前
这是一个用于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 类

?>
ohcc at 163 dot com
8年前
不要使用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 已经存在,则文件将保持不变。
}
?>
theking2(at)king.ma
5个月前
正如其他人所指出的,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。
raja at rsdisk dot com
6年前
zip->addfile 函数不会立即将文件添加到存档中。它会被排队并在 zip->close() 时执行。

我有一个目录中的备份文件。使用 foreach 语句,我将文件添加到存档中,然后立即将文件移动到转储目录。

这导致了错误。

<?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");
}
?>
[email protected]
15年前
如果在最近的Ubuntu更新后,ZipArchiveImproved出现警告,请将`reopen`函数中的`"self::CREATE"`替换为`"self::CREATE | self::OVERWRITE"`。

christophe
Farzad Ghanei
15年前
这是一个扩展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 [optional] $localname ZIP 存档中文件的名称
* @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()
}
?>
[email protected]
10 年前
`addFile()` 方法不接受 "file://" 协议。请去除 "file://" 并使用绝对路径。

我假设它也不接受任何其他协议,因为我在尝试添加文件时遇到问题,我正在构建的应用程序中的其他函数需要使用该协议。

另请注意,返回的状态码与任何预定义的错误码都不匹配,并且状态码永远不一样。我的假设是变量溢出,因为状态码在最小和最大 INT 值附近。
[email protected]
11年前
请注意,ZIP文件没有“文件夹”的概念。如果需要将数据存储到文件夹中,请在 `$localname` 中使用正斜杠 (“/”) 来分隔文件夹和文件名。

示例
$zip->addFile("test.txt", "mainfolder/subfolder/test.txt");
Dean Rather
12年前
此添加目录函数不需要您创建新的包装类,也不会将整个文件目录树添加到您的 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;
}

?>
shano
12年前
觉得它可能派上用场
递归添加目录中所有子目录和文件

class zip extends ZipArchive {

public function addDirectory($dir) { // 添加目录
foreach(glob($dir . '/*') as $file) {
if(is_dir($file))
$this->addDirectory($file);
else
$this->addFile($file);
}
}
}
Andreas R. newsgroups2005 at geekmail de
17年前
目前,使用 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 );
}
?>
ptipti at gala dot net
12年前
另一个意外,花费了很多麻烦。这样,在您将文件添加到存档后,可以安全地将其删除,利用了这样的结构

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% 的保证?
javierseixas at gmail dt com
16年前
我尝试添加文件时遇到几个问题,因为路径问题。错误提示是

ZipArchive::addFile() [function.ZipArchive-addFile]: Unable to access <path>

我使用了以“/”开头的绝对根路径,但不起作用。尝试以 "./" 开头您的路径(引用您网站的根目录)。
mike at thetroubleshooters dot dk
16年前
更糟糕的是,当您用完文件描述符时,它似乎会静默失败,我无法在任何日志文件中找到任何错误。
ohcc at 163 dot com
8年前
如果文件名包含汉字,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)));
}
// ...
stanleyshilov {} gmail.com
16年前
需要注意的是,上面提供的示例并不准确。

与 extractTo 不同,zip_open 不返回布尔值结果,因此上述示例将始终失败。
arrtedone at gmail dot com
11年前
请注意,ZipArchive::open() 不返回布尔值,而是整数,例如
$zip = new ZipArchive();
$filename = '/tmp/test.zip';
var_dum($zip->open($filename)); // 返回:int (11)
ss at littlerain dot com
15年前
请注意,文件实际上直到调用 $zip->close() 方法后才会添加到存档中。我花了很长时间才弄清楚为什么在通过 $zip->addFile() 添加大型文件后没有时间过去,但随后会使脚本超时。
pelpet at ic dot cz
14 年前
<?php
$zip
=new ZipArchive;
$zip->addFile('path to the file', 'new name of the file');
?>

当我想在存档前使用文件的原始名称将其添加到 zip 存档中时,我该怎么做?
(对不起,我来自捷克共和国,英语说得不太好,如果我哪里错了,请忽略。)
marco at maranao dot ca
15年前
这是我通过定期关闭/打开存档文件来解决文件描述符限制的解决方法。

<?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();
}
?>

希望对某人有所帮助。
To Top