PHP Conference Japan 2024

ZipArchive

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

简介

一个使用 Zip 压缩的文件档案。

类概要

class ZipArchive implements Countable {
/* 常量 */
public const int CREATE;
public const int EXCL;
public const int CHECKCONS;
public const int OVERWRITE;
public const int RDONLY;
public const int FL_NOCASE;
public const int FL_NODIR;
public const int FL_COMPRESSED;
public const int FL_UNCHANGED;
public const int FL_RECOMPRESS;
public const int FL_ENCRYPTED;
public const int FL_OVERWRITE;
public const int FL_LOCAL;
public const int FL_CENTRAL;
public const int FL_ENC_GUESS;
public const int FL_ENC_RAW;
public const int FL_ENC_STRICT;
public const int FL_ENC_UTF_8;
public const int FL_ENC_CP437;
public const int FL_OPEN_FILE_NOW;
public const int CM_DEFAULT;
public const int CM_STORE;
public const int CM_SHRINK;
public const int CM_REDUCE_1;
public const int CM_REDUCE_2;
public const int CM_REDUCE_3;
public const int CM_REDUCE_4;
public const int CM_IMPLODE;
public const int CM_DEFLATE;
public const int CM_DEFLATE64;
public const int CM_PKWARE_IMPLODE;
public const int CM_BZIP2;
public const int CM_LZMA;
public const int CM_LZMA2;
public const int CM_ZSTD;
public const int CM_XZ;
public const int CM_TERSE;
public const int CM_LZ77;
public const int CM_WAVPACK;
public const int CM_PPMD;
public const int ER_OK;
public const int ER_MULTIDISK;
public const int ER_RENAME;
public const int ER_CLOSE;
public const int ER_SEEK;
public const int ER_READ;
public const int ER_WRITE;
public const int ER_CRC;
public const int ER_ZIPCLOSED;
public const int ER_NOENT;
public const int ER_EXISTS;
public const int ER_OPEN;
public const int ER_TMPOPEN;
public const int ER_ZLIB;
public const int ER_MEMORY;
public const int ER_CHANGED;
public const int ER_COMPNOTSUPP;
public const int ER_EOF;
public const int ER_INVAL;
public const int ER_NOZIP;
public const int ER_INTERNAL;
public const int ER_INCONS;
public const int ER_REMOVE;
public const int ER_DELETED;
public const int ER_ENCRNOTSUPP;
public const int ER_RDONLY;
public const int ER_NOPASSWD;
public const int ER_WRONGPASSWD;
public const int ER_OPNOTSUPP;
public const int ER_INUSE;
public const int ER_TELL;
public const int ER_COMPRESSED_DATA;
public const int ER_CANCELLED;
public const int ER_DATA_LENGTH;
public const int ER_NOT_ALLOWED;
public const int ER_TRUNCATED_ZIP;
public const int AFL_RDONLY;
public const int AFL_IS_TORRENTZIP;
public const int AFL_WANT_TORRENTZIP;
public const int OPSYS_DOS;
public const int OPSYS_AMIGA;
public const int OPSYS_OPENVMS;
public const int OPSYS_UNIX;
public const int OPSYS_VM_CMS;
public const int OPSYS_ATARI_ST;
public const int OPSYS_OS_2;
public const int OPSYS_MACINTOSH;
public const int OPSYS_Z_SYSTEM;
public const int OPSYS_CPM;
public const int OPSYS_WINDOWS_NTFS;
public const int OPSYS_MVS;

public const int OPSYS_VSE;
public const int OPSYS_ACORN_RISC;
public const int OPSYS_VFAT;
public const int OPSYS_ALTERNATE_MVS;
public const int OPSYS_BEOS;
public const int OPSYS_TANDEM;
public const int OPSYS_OS_400;
public const int OPSYS_OS_X;
public const int OPSYS_DEFAULT;
public const int EM_NONE;
public const int EM_TRAD_PKWARE;
public const int EM_AES_128;
public const int EM_AES_192;
public const int EM_AES_256;
public const int EM_UNKNOWN;
public const string LIBZIP_VERSION;
public const int LENGTH_TO_END;
public const int LENGTH_UNCHECKED;
/* 属性 */
public readonly int $lastId;
public readonly int $status;
public readonly int $statusSys;
public readonly int $numFiles;
public readonly string $filename;
public readonly string $comment;
/* 方法 */
public addEmptyDir(string $dirname, int $flags = 0): bool
public addFile(
    string $filepath,
    string $entryname = "",
    int $start = 0,
    int $length = ZipArchive::LENGTH_TO_END,
    int $flags = ZipArchive::FL_OVERWRITE
): bool
public addFromString(string $name, string $content, int $flags = ZipArchive::FL_OVERWRITE): bool
public addGlob(string $pattern, int $flags = 0, array $options = []): array|false
public addPattern(string $pattern, string $path = ".", array $options = []): array|false
public clearError(): void
public close(): bool
public count(): int
public deleteIndex(int $index): bool
public deleteName(string $name): bool
public extractTo(string $pathto, array|string|null $files = null): bool
public getArchiveComment(int $flags = 0): string|false
public getArchiveFlag(int $flag, int $flags = 0): int
public getCommentIndex(int $index, int $flags = 0): string|false
public getCommentName(string $name, int $flags = 0): string|false
public getExternalAttributesIndex(
    int $index

,
    int &$opsys,
    int &$attr,
    int $flags = 0
): bool
public getExternalAttributesName(
    string $name,
    int &$opsys,
    int &$attr,
    int $flags = 0
): bool
public getFromIndex(int $index, int $len = 0, int $flags = 0): string|false
public getFromName(string $name, int $len = 0, int $flags = 0): string|false
public getNameIndex(int $index, int $flags = 0): string|false
public getStreamIndex(int $index, int $flags = 0): resource|false
public getStreamName(string $name, int $flags = 0): resource|false
public static isCompressionMethodSupported(int $method, bool $enc = true): bool
public static isEncryptionMethodSupported(int $method, bool $enc = true): bool
public locateName(string $name, int $flags = 0): int|false
public open(string $filename, int $flags = 0): bool|int
public registerProgressCallback(float $rate, callable $callback): bool
public renameIndex(int $index, string $new_name): bool
public renameName(string $name, string $new_name): bool
public replaceFile(
    string $filepath,
    int $index

,
    int $start = 0,
    int $length = ZipArchive::LENGTH_TO_END,
    int $flags = 0
): bool
public setArchiveComment(string $comment): bool
public setArchiveFlag(int $flag, int $value): bool
public setCommentIndex(int $index, string $comment): bool
public setCommentName(string $name, string $comment): bool
public setCompressionIndex(int $index, int $method, int $compflags = 0): bool
public setCompressionName(string $name, int $method, int $compflags = 0): bool
public setEncryptionIndex(int $index, int $method, #[\SensitiveParameter] ?string $password = null): bool
public setEncryptionName(string $name, int $method, #[\SensitiveParameter] ?string $password = null): bool
public setExternalAttributesIndex(
    int $index

,
    int $opsys,
    int $attr

,
    int $flags = 0
): bool
public setExternalAttributesName(
    string $name,
    int $opsys,
    int $attr

,
    int $flags = 0
): bool
public setMtimeIndex(int $index, int $timestamp, int $flags = 0): bool
public setMtimeName(string $name, int $timestamp, int $flags = 0): bool
public statIndex(int $index, int $flags = 0): array|false
public statName(string $name, int $flags = 0): array|false
public unchangeAll(): bool
public unchangeIndex(int $index): bool
public unchangeName(string $name): bool
}

属性

lastId

最后添加的条目(文件或目录)的索引值。从 PHP 8.0.0 和 PECL zip 1.18.0 开始可用。

status

Zip 归档的状态。适用于已关闭的归档文件,从 PHP 8.0.0 和 PECL zip 1.18.0 开始可用。

statusSys

Zip 归档的系统状态。适用于已关闭的归档文件,从 PHP 8.0.0 和 PECL zip 1.18.0 开始可用。

numFiles

归档文件中文件的数量

filename

文件系统中的文件名

comment

归档文件的注释

目录

添加注释

用户贡献的注释 15 条注释

58
umbalaconmeogia at NOSPAM dot gmail dot com
12 年前
压缩文件夹(包括自身)。
用法
HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');

<?php
class HZip
{
/**
* 将文件夹及其子目录中的文件添加到 zip 文件中。
* @param string $folder
* @param ZipArchive $zipFile
* @param int $exclusiveLength 要从文件路径中排除的文本数量。
*/
private static function folderToZip($folder, &$zipFile, $exclusiveLength) {
$handle = opendir($folder);
while (
false !== $f = readdir($handle)) {
if (
$f != '.' && $f != '..') {
$filePath = "$folder/$f";
// 添加到 zip 之前,从文件路径中移除前缀。
$localPath = substr($filePath, $exclusiveLength);
if (
is_file($filePath)) {
$zipFile->addFile($filePath, $localPath);
} elseif (
is_dir($filePath)) {
// 添加子目录。
$zipFile->addEmptyDir($localPath);
self::folderToZip($filePath, $zipFile, $exclusiveLength);
}
}
}
closedir($handle);
}

/**
* 压缩一个文件夹(包括自身)。
* 用法:
* HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
*
* @param string $sourcePath 要压缩的目录路径。
* @param string $outZipPath 输出 zip 文件的路径。
*/
public static function zipDir($sourcePath, $outZipPath)
{
$pathInfo = pathInfo($sourcePath);
$parentPath = $pathInfo['dirname'];
$dirName = $pathInfo['basename'];

$z = new ZipArchive();
$z->open($outZipPath, ZIPARCHIVE::CREATE);
$z->addEmptyDir($dirName);
self::folderToZip($sourcePath, $z, strlen("$parentPath/"));
$z->close();
}
}
?>
18
[email protected]
9 年前
使用 PHP 5.6+,您可能会遇到这些错误。

警告:Unknown: 无法在 Unknown 的第 0 行销毁 zip 上下文

警告:ZipArchive::close(): 无法删除文件:没有那个文件或目录,位于 xxxx.php 的第 xx 行

示例

警告:Unknown: 无法在 Unknown 的第 0 行销毁 zip 上下文

<?php
$za
= new ZipArchive;
$za->open('51-n.com.zip',ZipArchive::CREATE|ZipArchive::OVERWRITE);
?>

警告:ZipArchive::close(): 无法删除文件:没有那个文件或目录,位于 xxxx.php 的第 xx 行

<?php
$za
= new ZipArchive;
$za->open('51-n.com.zip',ZipArchive::CREATE|ZipArchive::OVERWRITE);
$za->close();
?>

当 zip 档案为空时,就会发生这种情况。
除非 zip 档案至少包含一个文件,否则它不会保存到磁盘上。此外,当应用 ZipArchive::OVERWRITE 时,如果存在同名文件,则在调用 ZipArchive::open() 后会将其删除。

因此,请不要忘记至少将一个文件添加到您的 zip 档案中。

<?php
$za
= new ZipArchive;
$za->open('51-n.com.zip',ZipArchive::CREATE|ZipArchive::OVERWRITE);
$za->addFromString('wuxiancheng.cn.txt','yes');
$za->close();
?>
2
[email protected]
1 年前
使用 DirectoryIterator 递归压缩文件夹的现代方法。我使用这个小程序类

<?php
class MakeZip
{
private
ZipArchive $zipArchive;
private
int $startPathLength; // 存储实体的起始字符数

public function __construct(
string $zipArchivename,
public readonly
string $startPath,
public readonly
mixed $convert_function,
)
{
$this->zipArchive = new \ZipArchive;
$this->zipArchive->open($zipArchivename, ZipArchive::CREATE);
$this->startPathLength = strlen($this-> startPath);

$this-> zipDir($startPath);
}
public function
__destruct()
{
$this-> zipArchive-> close();
}

/**
* 将文件夹中的文件和子目录添加到 zip 文件中。
* @param string $folder
* @param ZipArchive $zipFile
* @param int $exclusiveLength 要从文件路径中排除的文本数量。
*/
private function zipDir($folder)
{
echo
$folder . '<br>' . PHP_EOL;
foreach (new
\DirectoryIterator($folder) as $f) {
if (
$f->isDot())
continue;
// 跳过 . ..
if ($f->isDir()) {
if(
$f->getExtension() === 'git') continue; // 跳过 .git 文件夹
$this-> zipArchive-> addEmptyDir($f->getPathname());
$this-> zipDir($f->getPathname());

continue;
}
if (
$f->isFile()) {
if (
$f->getBasename() === basename(__FILE__)) continue; // 跳过自身
if ($f->getExtension() === 'zip') continue; // 跳过 ZIP 文件

$this-> zipArchive ->addFile( substr($f-> getPathname(), $this-> startPathLength) ); // 删除 './'

continue;
}

}
}
}
?>

可以这样使用
<?php
$host
= str_replace('.', '_', $_SERVER['HTTP_HOST']);
$date = date('Ymd-His');

$zip = new \MakeZip("./archiv-$host-$date.zip", './', $convert_function);
unset(
$zip);
?>

在构造函数中添加加密或其他功能。
9
AshleyDambra at live dot com
11 年前
简单的 xZip 类,用于将大型文件夹压缩成多个部分,并一次解压缩多个 zip 文件。

<?php
class xZip {
public function
__construct() {}
private function
_rglobRead($source, &$array = array()) {
if (!
$source || trim($source) == "") {
$source = ".";
}
foreach ((array)
glob($source . "/*/") as $key => $value) {
$this->_rglobRead(str_replace("//", "/", $value), $array);
}

foreach ((array)
glob($source . "*.*") as $key => $value) {
$array[] = str_replace("//", "/", $value);
}
}
private function
_zip($array, $part, $destination) {
$zip = new ZipArchive;
@
mkdir($destination, 0777, true);

if (
$zip->open(str_replace("//", "/", "{$destination}/partz{$part}.zip"), ZipArchive::CREATE)) {
foreach ((array)
$array as $key => $value) {
$zip->addFile($value, str_replace(array("../", "./"), NULL, $value));
}
$zip->close();
}
}
public function
zip($limit = 500, $source = NULL, $destination = "./") {
if (!
$destination || trim($destination) == "") {
$destination = "./";
}

$this->_rglobRead($source, $input);
$maxinput = count($input);
$splitinto = (($maxinput / $limit) > round($maxinput / $limit, 0)) ? round($maxinput / $limit, 0) + 1 : round($maxinput / $limit, 0);

for(
$i = 0; $i < $splitinto; $i ++) {
$this->_zip(array_slice($input, ($i * $limit), $limit, true), $i, $destination);
}

unset(
$input);
return;
}
public function
unzip($source, $destination) {
@
mkdir($destination, 0777, true);

foreach ((array)
glob($source . "/*.zip") as $key => $value) {
$zip = new ZipArchive;
if (
$zip->open(str_replace("//", "/", $value)) === true) {
$zip->extractTo($destination);
$zip->close();
}
}
}

public function
__destruct() {}
}

//$zip = new xZip;
//$zip->zip(500, "images/", "images_zip/");
//$zip->unzip("images_zip/", "images/");
?>
10
Jerry dot Saravia at emc dot com
13 年前
以下代码可用于获取 zip 文件中所有文件名的列表。

<?php
$za
= new ZipArchive();

$za->open('theZip.zip');

for(
$i = 0; $i < $za->numFiles; $i++ ){
$stat = $za->statIndex( $i );
print_r( basename( $stat['name'] ) . PHP_EOL );
}
?>
13
[email protected]
9 年前
一种压缩文件并在之后下载它们的方法
<?php

$files
= array('image.jpeg','text.txt','music.wav');
$zipname = 'enter_any_name_for_the_zipped_file.zip';
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
foreach (
$files as $file) {
$zip->addFile($file);
}
$zip->close();

///然后下载压缩文件。
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zipname);
header('Content-Length: ' . filesize($zipname));
readfile($zipname);

?>
4
[email protected]
14年前
将存档中的文件读取到变量中。
如果发生CRC32不匹配,则会自动打印警告,我们捕获该警告,以便可以打印我们自己的错误消息。

<?php
$zip
= new ZipArchive();
if (
$zip->open('archive.zip')) {
$fp = $zip->getStream('myfile.txt'); //存档内的文件
if(!$fp)
die(
"Error: can't get stream to zipped file");
$stat = $zip->statName('myfile.txt');

$buf = ""; //文件缓冲区
ob_start(); //捕获CRC错误消息
while (!feof($fp)) {
$buf .= fread($fp, 2048); //读取超过2156个字节似乎会禁用内部CRC32验证(错误?)
}
$s = ob_get_contents();
ob_end_clean();
if(
stripos($s, "CRC error") != FALSE){
echo
'CRC32不匹配,当前 ';
printf("%08X", crc32($buf)); //当前CRC
echo ', 预期 ';
printf("%08X", $stat['crc']); //预期CRC
}

fclose($fp);
$zip->close();
//完成,解压缩的文件存储在$buf中
}
?>

要创建损坏的文件,请使用十六进制编辑器更改zip文件中的一个字节。
8
[email protected]
12 年前
有一个有用的函数可以将ZipArchive状态作为人类可读的字符串获取

<?php
function ZipStatusString( $status )
{
switch( (int)
$status )
{
case
ZipArchive::ER_OK : return 'N 无错误';
case
ZipArchive::ER_MULTIDISK : return 'N 不支持多磁盘zip存档';
case
ZipArchive::ER_RENAME : return 'S 重命名临时文件失败';
case
ZipArchive::ER_CLOSE : return 'S 关闭zip存档失败';
case
ZipArchive::ER_SEEK : return 'S 搜索错误';
case
ZipArchive::ER_READ : return 'S 读取错误';
case
ZipArchive::ER_WRITE : return 'S 写入错误';
case
ZipArchive::ER_CRC : return 'N CRC错误';
case
ZipArchive::ER_ZIPCLOSED : return 'N 包含的zip存档已关闭';
case
ZipArchive::ER_NOENT : return 'N 没有此文件';
case
ZipArchive::ER_EXISTS : return 'N 文件已存在';
case
ZipArchive::ER_OPEN : return 'S 无法打开文件';
case
ZipArchive::ER_TMPOPEN : return 'S 无法创建临时文件';
case
ZipArchive::ER_ZLIB : return 'Z Zlib错误';
case
ZipArchive::ER_MEMORY : return 'N 内存分配失败';
case
ZipArchive::ER_CHANGED : return 'N 条目已更改';
case
ZipArchive::ER_COMPNOTSUPP : return 'N 不支持压缩方法';
case
ZipArchive::ER_EOF : return 'N 文件末尾';
case
ZipArchive::ER_INVAL : return 'N 无效参数';
case
ZipArchive::ER_NOZIP : return 'N 不是zip存档';
case
ZipArchive::ER_INTERNAL : return 'N 内部错误';
case
ZipArchive::ER_INCONS : return 'N zip存档不一致';
case
ZipArchive::ER_REMOVE : return 'S 无法删除文件';
case
ZipArchive::ER_DELETED : return 'N 条目已被删除';

default: return
sprintf('未知状态 %s', $status );
}
}

?>
4
[email protected]
13 年前
这是一个简单的函数,它可以压缩包含所有子文件夹的文件夹或仅压缩一个简单文件……$data变量可以是字符串或数组……

<?php
public function un_zip($data,$arcpf,$mode='zip',$obj=''){
$absoluterpfad = 'YOUR_BASE_PATH';
$arcpf = $absoluterpfad.DS.$arcpf;
if(
is_object($obj)==false){
$archiv = new ZipArchive();
$archiv->open($arcpf,ZipArchive::CREATE);
}else{
$archiv =& $obj;}
if(
$mode=='zip'){
if(
is_array($data)==true){
foreach(
$data as $dtmp){
$archiv =& un_zip($dtmp,$arcpf,'zip',&$archiv);
}
}else{
if(
is_dir($data)==true){
$archiv->addEmptyDir(str_replace($absoluterpfad.DS,'',$data));
$files = scandir($data);
$bad = array('.','..');
$files = array_diff($files,$bad);
foreach(
$files as $ftmp){
if(
is_dir($data.DS.$ftmp)==true){
$archiv->addEmptyDir(str_replace($absoluterpfad.DS,'',$data.'/'.$ftmp));
$archiv =& un_zip($data.DS.$ftmp,$arcpf,'zip',&$archiv);
}elseif(
is_file($data.DS.$ftmp)==true){
$archiv->addFile($data.DS.$ftmp,str_replace($absoluterpfad.DS,'',$data.'/'.$ftmp));
}
}
}elseif(
is_file($data)==true){$archiv->addFile($data,str_replace($absoluterpfad.DS,'',$data));}
}
}
if(
is_object($obj)==false){$archiv->close();}
else{return
$archiv;}
if(
$mode=='unzip'){$archiv->extractTo($data);}
}
?>
4
[email protected]
9 年前
PHP 5.3.3 版本存在限制(似乎在后续版本中已解决;5.3.29 在另一台服务器上运行正常)。

如果您尝试打开包含超过 65,535 个文件的 zip 文件(在我的案例中,它有 237,942 个文件),则无法访问后面的文件。numFiles 属性仅报告前 65k 个文件。
3
[email protected]
12 年前
您好。
我刚刚编写了一个小函数来压缩整个文件夹,同时保持目录结构。希望它能帮助到其他人。

<?php
function folderToZip($folder, &$zipFile, $subfolder = null) {
if (
$zipFile == null) {
// 未提供资源,退出
return false;
}
// 检查 $folder 是否以斜杠结尾,如果不是,则添加一个
$folder .= end(str_split($folder)) == "/" ? "" : "/";
$subfolder .= end(str_split($subfolder)) == "/" ? "" : "/";
// 首先遍历 $folder 中的所有文件
$handle = opendir($folder);
while (
$f = readdir($handle)) {
if (
$f != "." && $f != "..") {
if (
is_file($folder . $f)) {
// 如果找到文件,则存储它
// 如果有子文件夹,则存储在该文件夹中
if ($subfolder != null)
$zipFile->addFile($folder . $f, $subfolder . $f);
else
$zipFile->addFile($folder . $f);
} elseif (
is_dir($folder . $f)) {
// 如果找到文件夹,则在 zip 中创建文件夹
$zipFile->addEmptyDir($f);
// 并再次调用函数
folderToZip($folder . $f, $zipFile, $f);
}
}
}
}
?>

使用方法如下
<?php
$z
= new ZipArchive();
$z->open("test.zip", ZIPARCHIVE::CREATE);
folderToZip("storeThisFolder", $z);
$z->close();
?>

祝您愉快!
1
[email protected]
12 年前
重要提示:由于 zip 文件的自然文件大小限制为 4GB(准确地说是约 3.6GB),如果结果大于 4GB,则此类将生成损坏的文件。使用 tar.gz 是一个合适的替代方案。
0
[email protected]
9 年前
<?php
// 使用 bzip2 + ZipArchive 减少 zip 档案的文件大小。
$zip = new ZipArchive;
$zip->open('i.zip',ZIPARCHIVE::CREATE|ZIPARCHIVE::OVERWRITE);
$file='wuxiancheng.cn.sql';
$bzFilename = $file.'.bz2';
$sql = file_get_contents($file);
$sql = bzcompress($sql,9);
$zip->addFromString($bzFilename,$sql);
$zip->setArchiveComment('zipped on '.date('Y-M-d'));
?>
-2
[email protected]
10 年前
您可以检查通用标志以测试 zip 文件是否已加密。以下示例函数。

<?php

/**
* 检查文件是否已加密
*
* 注意:如果文件不存在或无法打开,函数
* 也将返回 false。
*
* @param string $pathToArchive
* @return boolean 返回 true 表示文件已加密
*/
function isEncryptedZip( $pathToArchive ) {
$fp = @fopen( $pathToArchive, 'r' );
$encrypted = false;
if (
$fp && fseek( $fp, 6 ) == 0 ) {
$string = fread( $fp, 2 );
if (
false !== $string ) {
$data = unpack("vgeneral", $string);
$encrypted = $data[ 'general' ] & 0x01 ? true : false;
}
fclose( $fp );
}
return
$encrypted;
}
-4
h-fate at gmx dot net
14年前
请注意,有多种算法可以生成 zip 文件。例如,我发现使用 ZipArchive 创建的 Office OpenXML 文件无法被 Excel 2007 识别。

在这种情况下,您必须使用不同的类来压缩文件,例如 PclZip。
To Top