PHP操作ZIP之ZipArchive类以及如何避免生成压缩文件带有目录层级的问题

常用的方法

php ZipArchive可以说是php自带的一个函数了,他可对对文件进行压缩与解压缩处理,但是使用此类之前我们必须在php.ini中把extension=php_zip.dll前面的分号有没有去掉,然后再重启Apache这样才能使用这个类库。

ziparchive 可选参数,更多的使用例子,参考PHP - Manual: ZipArchive - 互联网笔记

ZipArchive::addEmptyDir

添加一个新的文件目录

php 复制代码
<?php
$zip = new ZipArchive;
if ($zip->open('test.zip') === TRUE) {
    if($zip->addEmptyDir('newDirectory')) {
        echo 'Created a new root directory';
    } else {
        echo 'Could not create the directory';
    }
    $zip->close();
} else {
    echo 'failed';
}
?>

ZipArchive::addFile

将文件添加到指定zip压缩包中。

ZipArchive::addFromString

添加的文件同时将内容添加进去

ZipArchive::close

关闭ziparchive

ZipArchive::extractTo

将压缩包解压

ZipArchive::open

打开一个zip压缩包

ZipArchive::getStatusString

返回压缩时的状态内容,包括错误信息,压缩信息等等

ZipArchive::deleteIndex

删除压缩包中的某一个文件,如:deleteIndex(0)删除第一个文件

ZipArchive::deleteName

删除压缩包中的某一个文件名称,同时也将文件删除。

ZipArchive::open

php 复制代码
<?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 'failed';
}
?>
php 复制代码
<?php
$name = tempnam(sys_get_temp_dir(), "FOO");
$zip = new ZipArchive;
$res = $zip->open($name, ZipArchive::OVERWRITE); /* truncate as empty file is not valid */
if ($res === TRUE) {
    $zip->addFile('data.txt', 'entryname.txt');
    $zip->close();
    echo 'ok';
} else {
    echo 'failed';
}
?>

基本使用例

解压缩zip文件

注意,解压的文件夹中不要有中文!会引起乱码!

php 复制代码
$zip = new ZipArchive;//新建一个ZipArchive的对象
/*
通过ZipArchive的对象处理zip文件
$zip->open这个方法的参数表示处理的zip文件名。
如果对zip文件对象操作成功,$zip->open这个方法会返回TRUE
*/

if ($zip->open('test.zip'))
{
    $zip->extractTo('images');//假设解压缩到在当前路径下images文件夹的子文件夹php
    $zip->close();//关闭处理的zip文件
}

文件追加内容添加到zip文件

php 复制代码
zip = new ZipArchive;
$res = $zip->open('test.zip', ZipArchive::CREATE);
if ($res) {
    $zip->addFromString('test.txt', 'file content goes here');
    $zip->close();
    echo 'ok';
} else {
  echo 'failed';
}

建议:$zip->open 使用try-catch去捕捉

php 复制代码
try {
   $zipResult = $zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE);
    //关闭处理的zip文件
    $zip->close();
}  catch (Exception $e) {
    exit( $e->getMessage());
}

将服务器上的文件夹打包成zip文件

php 复制代码
function addFileToZip($path, $zip) {

    $handler = opendir($path); //打开当前文件夹由$path指定。
    
    /*
    循环的读取文件夹下的所有文件和文件夹
    其中$filename = readdir($handler)是每次循环的时候将读取的文件名赋值给$filename,
    为了不陷于死循环,所以还要让$filename !== false。
    一定要用!==,因为如果某个文件名如果叫'0',或者某些被系统认为是代表false,用!=就会停止循环
    */
    
    while (($filename = readdir($handler)) !== false) {
        //文件夹文件名字为'.'和'..',不要对他们进行操作
        if ($filename != "." && $filename != "..") {
            $filePath = "{$path}/{$filename}";
            // 如果读取的某个对象是文件夹,则递归
            if (is_dir($filePath)) {
                addFileToZip($filePath, $zip);
            } else { 
                var_dump($filePath);
                // 将文件加入zip对象 传入第二个参数是避免出现目录层级的问题
                $zip->addFile($filePath, pathinfo($filePath, PATHINFO_BASENAME));
            }
        }
    }
    @closedir($path);
}
$STATICS_PATH = 'd:/xampp/htdocs/xin-card';
$templatePath = '2023/03/0d9cf19188485dc70e21a1aae4ffad8d';
// 要下载文件的最终目录
$finalPath = "{$STATICS_PATH}/templates/{$templatePath}";

if(!file_exists($finalPath)) {
    exit('路径不存在');
}

$zip = new ZipArchive();
$zipName = time() . '.zip';
// d:/xampp/htdocs/xin-card/zip/187823213.zip
$zipPath = "{$STATICS_PATH}/zip/{$zipName}";
try {
    $zipResult = $zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE);
    //调用方法,对要打包的根目录进行操作,并将ZipArchive的对象传递给方法
    addFileToZip($finalPath, $zip); 
    //关闭处理的zip文件
    $zip->close();
}  catch (Exception $e) {
    exit( $e->getMessage());
}

解决生成压缩文件带有目录层级的问题

比如上面的addFileToZip方法,我一开始是这么写的:

php 复制代码
if (is_dir($filePath)) {
       addFileToZip($filePath, $zip);
} else { 
       $zip->addFile($filePath);
}

这样写最终导致的后果就是生成的压缩文件,里面带有目录层级!

像这样:D:\xampp\htdocs\xin-card\statics\templates-packages\1679986315\D_\xampp\htdocs\xin-card\statics\templates-packages\2023\03\0d9cf19188485dc70e21a1aae4ffad8d

实际上只有最后的文件夹才是我想要的!

如果你使用php ZipArchive addFile 方法把多个文件压缩在1个目录时会产生一个问题,我们只想要在当前目录把所有文件放在一起,结果他安装每个文件的所在目录在当前目录创建一遍,解决方式如下:

php 复制代码
$allAttachment = [
    '1.png',   
    '2.png',   
    '3.png',   
    '4.png',    
];

// 循环保存文件到Zip中
foreach ($allAttachment as $attachmentItem) {
    $rootpath = 'd:/xampp/htdocs/xin-cards/';
    if ($attachmentItem) {
        $attachmentItem = "$rootpath/$attachmentItem";
        // 添加文件
        $zip->addFile($attachmentItem);
        // 对添加的文件重新命名,避免出现目录问题
        $zip->renameName($attachmentItem, basename($attachmentItem));
    }
}

// 关闭
$zip->close();
如果不能解决您的问题,可以尝试如下方式

// 添加文件
$zip->addFile($attachmentItem, pathinfo($attachmentItem, PATHINFO_BASENAME));

浏览器下载并删除压缩文件

以下是打包服务器上某个文件夹中所有文件,并下载的然后再删除压缩文件的全部代码:

php 复制代码
<?php
$STATICS_PATH = PATH['statics'];
$templatePath = '2023/03/0d9cf19188485dc70e21a1aae4ffad8d';
$finalPath = "{$STATICS_PATH}/templates-packages/{$templatePath}";
if(!file_exists($finalPath)) {
    exit('路径不存在');
}

function addFileToZip($path, $zip) {

    $handler = opendir($path); //打开当前文件夹由$path指定。
    
    /*
    循环的读取文件夹下的所有文件和文件夹
    其中$filename = readdir($handler)是每次循环的时候将读取的文件名赋值给$filename,
    为了不陷于死循环,所以还要让$filename !== false。
    一定要用!==,因为如果某个文件名如果叫'0',或者某些被系统认为是代表false,用!=就会停止循环
    */
    
    while (($filename = readdir($handler)) !== false) {
        //文件夹文件名字为'.'和'..',不要对他们进行操作
        if ($filename != "." && $filename != "..") {
            $filePath = "{$path}/{$filename}";
            // 如果读取的某个对象是文件夹,则递归
            if (is_dir($filePath)) {
                addFileToZip($filePath, $zip);
            } else { 
                // 将文件加入zip对象 传入第二个参数是避免出现目录层级的问题
                $zip->addFile($filePath, pathinfo($filePath, PATHINFO_BASENAME));
            }
        }
    }
    @closedir($path);
}

$zip = new ZipArchive();
$zipName = time() . '.zip';
$zipPath = "{$STATICS_PATH}/templates-packages/{$zipName}";
try {
    $zipResult = $zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE);
    //调用方法,对要打包的根目录进行操作,并将ZipArchive的对象传递给方法
    addFileToZip($finalPath, $zip); 
    //关闭处理的zip文件
    $zip->close();

    //下载文件
    $file = fopen($zipPath, "r");
    //返回的文件类型
    Header("Content-type: application/octet-stream");
    //按照字节大小返回
    Header("Accept-Ranges: bytes");
    //返回文件的大小
    Header("Accept-Length: " . filesize($zipPath));
    //这里设置客户端的弹出对话框显示的文件名
    Header("Content-Disposition: attachment; filename=" . $zipName);
    //一次性将数据传输给客户端
    //echo fread($file, filesize($filePath));
    //一次只传输1024个字节的数据给客户端
    //向客户端回送数据
    $buffer = 1024;//
    //判断文件是否读完
    while (!feof($file)) {
        //将文件读入内存
        $file_data = fread($file, $buffer);
        //每次向客户端回送1024个字节的数据
        echo $file_data;
 
    }
    //将生成的zip文件在服务器端删除,只需要客户端下载就行了
    @unlink($zipPath);

}  catch (Exception $e) {
    exit( $e->getMessage());
}

相关资料

解决phpZipArchive生成压缩文件带有目录层级的问题【阿里开发者社区】

解决phpZipArchive生成压缩文件带有目录层级的问题

php开启ziparchivephpZipArchive类使用实例详解

相关推荐
清风fu杨柳7 分钟前
centos7 arm版本编译qt5.6.3详细说明
开发语言·arm开发·qt
醉颜凉10 分钟前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
_小柏_12 分钟前
C/C++基础知识复习(20)
开发语言
程序员小明z22 分钟前
基于Java的药店管理系统
java·开发语言·spring boot·毕业设计·毕设
我是哈哈hh1 小时前
HTML5和CSS3的进阶_HTML5和CSS3的新增特性
开发语言·前端·css·html·css3·html5·web
Dontla1 小时前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust
Neophyte06082 小时前
C++算法练习-day40——617.合并二叉树
开发语言·c++·算法
慕容复之巅2 小时前
基于MATLAB的条形码的识别图像处理报告
开发语言·图像处理·matlab
云空2 小时前
《InsCode AI IDE:编程新时代的引领者》
java·javascript·c++·ide·人工智能·python·php
zqzgng2 小时前
Python 数据可视化pilot
开发语言·python·信息可视化