php zip压缩

2021-03-18
/**
 * @description: zip压缩,压缩/解压缩
 */
class ZIPCompress
{
    /**
     * @description: 
     * @var ZipArchive 
     */
    protected $zip;

    public function __construct($filename = '', $flag = 0)
    {
        if ($filename) {
            $this->open($filename, $flag);
        }
    }

    public function __destruct()
    {
        // 关闭打开或创建的存档并保存更改。失败返回false
        $this->zip->close();
    }

    /**
     * @description: 打开新的或现有的zip存档以进行读取,写入或修改,成功返回true,失败将返回指定错误代码
     * @param string $filename 文件名
     * @param int $flag 
     *                  ZipArchive::OVERWRITE 8  打开新的压缩包,覆盖现有文件
     *                  ZipArchive::CREATE    1  如果不存在创建一个新压缩包
     *                  ZipArchive::RDONLY    16 只读模式打开压缩包
     *                  ZipArchive::EXCL      2  如果压缩包已经存在,则出错
     *                  ZipArchive::CHECKCONS 4  对压缩包执行额外的一致性检查,如果失败则显示错误
     * @return int|true
     */
    public function open($filename, $flag = 0)
    {
        $this->zip = new ZipArchive;
        $res       = $this->zip->open($filename, $flag);
        if ($res !== true) {
            throw new Exception($this->zipErorr($res));
        }
    }

    /**
     * @description: 给zip文件加密
     * @param string $password 密码
     * @return {*}
     */
    public function encryption($password)
    {
        return $this->zip->setPassword($password);
    }

    /**
     * @description: 将文件解压缩到指定目录,压缩包中的中文解决不了
     * @param string $dir 目录,默认当前目录
     * @return bool
     */
    public function uncompress($dir = './')
    {
        !is_dir($dir) && mkdir($dir, 0777, true);
        $dir = realpath($dir);
        return $this->zip->extractTo($dir);
    }

    /**
     * @description: 自定义解压缩,支持中文,支持递归解压缩
     * @param string $dir 解压后的目录,默认当前目录
     * @return bool
     */
    public function uncompress_custom($dir = './')
    {
        !is_dir($dir) && mkdir($dir, 0777, true);
        $dir = str_replace('\\', '/', realpath($dir)) . '/';
        
        // 获取压缩包中文档数量
        $count    = version_compare(PHP_VERSION, '7.2') > -1 ? $this->zip->count() : $this->zip->numFiles;
        $encoding = ['utf-8', 'gbk', 'big5', 'cp936'];
        for ($i = 0; $i < $count; $i++) {
            // 根据索引获取文件详细信息,解决中文乱码
            $files    = $this->zip->statIndex($i, ZipArchive::FL_ENC_RAW);
            $encode   = mb_detect_encoding($files['name'], $encoding);
            $filename = iconv($encode, 'utf-8//ignore', $files['name']);
            $file     = $dir . $filename;
            if ($filename[-1] == '/') {
                !is_dir($file) && mkdir($file, 0777, true);
            } else {
                $path = dirname($file);
                $path && !is_dir($path) && mkdir($path, 0777, true);

                file_put_contents($file, $this->zip->getFromIndex($i));
                
                $ext = pathinfo($file, PATHINFO_EXTENSION);
                if ($ext == 'zip') {
                    $zip  = new self($file);
                    $dir1 = str_replace('.' . $ext, '', $file);
                    !is_dir($dir1) && mkdir($dir1, 0777, true);
                    $zip->uncompress_custom($dir1);
                    unset($zip);
                    @unlink($file);
                }
            }
        }
    }

    /**
     * @description: 压缩指定目录或指定文件
     * @param string $dir 要压缩的目录,默认为当前目录
     * @param array $files 要压缩的文件数组,[['name'=>'','path'=>'']]$files指定时,$dir参数不生效
     * @param string $gen_dir 生成压缩包位置,默认当前位置
     * @param string $name 压缩后的文件名,默认自动生成
     * @return bool
     */
    public function compress($dir = './', $files = [], $gen_dir = './', $name = null)
    {
        !is_dir($gen_dir) && mkdir(iconv('utf-8', 'gbk', $gen_dir), 0777, true);
        $zipname = $gen_dir . ($name ?: (time() . bin2hex(random_bytes(8)))) . '.zip';
        $this->open($zipname, \ZipArchive::CREATE);
        if ($files) {
            foreach ($files as $file) {
                if (empty($file['path']) || !file_exists($file['path'])) continue;
                $this->zip->addFile($file['path'], $file['name']);
            }
        } else {
            $this->_dir_zip($this->zip, $dir);
        }
        return $zipname;
    }

    /**
     * @description: 递归压缩目录
     * @param object $zip zip对象
     * @param string $dir 要压缩的目录
     * @param string $zip_dir 在压缩包中要创建的目录
     * @return {*}
     */
    private function _dir_zip($zip, $dir, $zip_dir = '')
    {
        $dir = str_replace('\\', '/', realpath($dir)) . '/';
        if ($files = opendir($dir)) {
            while (($file = readdir($files)) !== false) {
                if ($file == '.' || $file == '..') continue;
                $path = $dir . $file;
                if (is_dir($path)) {
                    $temp_dir = $zip_dir . $file . '/';
                    // 添加一个空目录,失败时返回false
                    $zip->addEmptyDir($temp_dir);
                    $this->_dir_zip($zip, $path, $temp_dir);
                } else {
                    $zip->addFile($zip_dir . $file, $path);
                }
            }
        }
    }

    /**
     * @description: 获取zip错误
     * @param int $error 错误编码
     * @return {*}
     */
    protected function zipErorr($error)
    {
        return [
            ZipArchive::ER_EXISTS => '文件已存在',
            ZipArchive::ER_INCONS => 'zip压缩包不一致',
            ZipArchive::ER_INVAL  => '无效的论点',
            ZipArchive::ER_MEMORY => 'Malloc失败',
            ZipArchive::ER_NOENT  => '文件不存在',
            ZipArchive::ER_NOZIP  => '不是zip压缩包',
            ZipArchive::ER_OPEN   => '无法打开文件',
            ZipArchive::ER_READ   => '读取错误',
            ZipArchive::ER_SEEK   => '寻址错误',
        ][$error] ?? '';

    }
}

 

{/if}