• PHP支持多格式的解压缩工具类


    一、 引语

    本人在做一个企业云盘项目当中,遇到一个文件在线解压缩的需求,查了网上很多资料,但都是只支持单一格式或部分格式,固创建了本工具类,对市面上主流的压缩格式进行集成支持,并且简单易用。

     

    二、 功能

    1. 支持zip、rar、phar、tar、gz、bz2、7z格式的解压

    2. 支持对单文件、多文件、文件夹进行压缩成zip文件格式数据库连接池

     

    三、 前置条件

    1. 安装php_zip插件:用于解压缩zip格式文件

    2. 安装php_rar插件:用于解压缩rar格式文件

    3. 安装php_phar插件:用于解压缩phar、tar、gz、bz2格式文件

    4. 安装p7zip p7zip-full软件:用于解压缩7z格式文件

     

    四、 实现

    class ZipUtil
    
    {
    
        /**
    
         * 解压
    
         * @param string $zipFilePath 压缩文件路径
    
         * @param string $toDirPath 解压目录路径
    
         * @return string
    
         * @throws Exception
    
         */
    
        public static function extract(string $zipFilePath, string $toDirPath)
    
        {
    
            $toDirPath = rtrim($toDirPath, '/');
    
    
    
    
            self::deleteDir($toDirPath, false);
    
            if (!is_file($zipFilePath)) throw new Exception('文件不存在。');
    
            if (!is_dir($toDirPath)) {
    
                mkdir($toDirPath, 0777, true);
    
            }
    
    
    
    
            $zipFilePathInfo = pathinfo($zipFilePath);
    
    
    
    
            $zipExt = pathinfo($zipFilePath, PATHINFO_EXTENSION);
    
      
    
            switch ($zipExt) {
    
                case 'zip':
    
                    if (!class_exists('ipArchive')) throw new Exception('未安装Zip插件。');
    
    
    
    
                    $zipArch = new ipArchive();
    
                    if ($zipArch->open($zipFilePath) !== true) throw new Exception('解压失败。');
    
    
    
    
                    //$zipArch->extractTo($toDirPath); //这个中文会乱码
    
    
    
    
                    //解决中文会乱码
    
                    $fileNum = $zipArch->numFiles;
    
                    for ($i = 0; $i < $fileNum; ++$i) {
    
                        $statInfo = $zipArch->statIndex($i, ipArchive::FL_ENC_RAW);
    
                        $statInfo['name'] = self::convertToUtf8($statInfo['name']);
    
                        //print_r($statInfo);
    
                        if ($statInfo['crc'] === 0 && $statInfo['name'][strlen($statInfo['name']) - 1] === '/') {
    
                            $dirPath = $toDirPath . '/' . $statInfo['name'];
    
                            if (!is_dir($dirPath)) {
    
                                mkdir($dirPath, 0777, true);
    
                            }
    
                        } else {
    
                            copy('zip://' . $zipFilePath . '#' . $zipArch->getNameIndex($i), $toDirPath . '/' . $statInfo['name']);
    
                        }
    
                    }
    
                    $zipArch->close();
    
                    break;
    
                case 'rar':
    
                    if (!class_exists('RarArchive')) throw new Exception('未安装Rar插件。');
    
    
    
    
                    $rarArch = RarArchive::open($zipFilePath);
    
                    if ($rarArch === false) throw new Exception('解压失败。');
    
    
    
    
                    $entries = $rarArch->getEntries();
    
                    if ($entries === false) throw new Exception('解压失败。');
    
    
    
    
                    foreach ($entries as $entry) {
    
                        $entry->extract($toDirPath);
    
                    }
    
                    $rarArch->close();
    
                    break;
    
                case 'phar':
    
                    if (!class_exists('Phar')) throw new Exception('未安装Phar插件。');
    
                    $phar = new Phar($zipFilePath, null, null);
    
                    $extract = $phar->extractTo($toDirPath, null, true);
    
                    if (!isset($zipFilePathInfo['extension'])) {
    
                        unlink($zipFilePath);
    
                    }
    
                    if ($extract === false) throw new Exception('解压失败。');
    
                    break;
    
                case 'tar':
    
                case 'gz':
    
                case 'bz2':
    
                    if (!class_exists('PharData')) throw new Exception('未安装Phar插件。');
    
                    $formats = [
    
                        'tar' => Phar::TAR,
    
                        'gz' => Phar::GZ,
    
                        'bz2' => Phar::BZ2,
    
                    ];
    
                    $format = $formats[$zipExt];
    
                    $phar = new PharData($zipFilePath, null, null, $format);
    
                    $extract = $phar->extractTo($toDirPath, null, true);
    
                    if (!isset($zipFilePathInfo['extension'])) {
    
                        unlink($zipFilePath);
    
                    }
    
                    if ($extract === false) throw new Exception('解压失败。');
    
                    break;
    
                case '7z':
    
                    if(shell_exec('type 7z') === null) throw new Exception('未安装p7zip软件。');
    
                    $cmd = '7z x ' . $zipFilePath . ' -r -o' . $toDirPath;
    
                    $result = shell_exec($cmd);
    
                    break;
    
                default:
    
                    throw new Exception('不支持的解压格式。');
    
            }
    
    
    
    
            return $toDirPath;
    
        }
    
    
    
    
        /**
    
         * 压缩多个文件
    
         * @param array $files 文件列表,格式:[['file_type'=>'file|folder', 'file_path'=>'/a/b/test.txt', 'local_name'=>'b/test.txt']]
    
         * @param string $toFilePath 压缩文件路径
    
         * @return string
    
         * @throws Exception
    
         */
    
        public static function package(array $files, string $toFilePath)
    
        {
    
            $toFilePathInfo = pathinfo($toFilePath);
    
            if (!is_dir($toFilePathInfo['dirname'])) {
    
                mkdir($toFilePathInfo['dirname'], 0777, true);
    
            }
    
    
    
    
            $zipArch = new ipArchive();
    
            if ($zipArch->open($toFilePath, ipArchive::CREATE) !== true) throw new Exception('压缩失败。');
    
    
    
    
            foreach ($files as $file) {
    
                if ($file['file_type'] === 'folder') {
    
                    $zipArch->addEmptyDir(ltrim($file['local_name'], '/'));
    
                } else if ($file['file_type'] === 'file') {
    
                    if (is_file($file['file_path'])) {
    
                        $zipArch->addFile($file['file_path'], $file['local_name']);
    
                    }
    
                }
    
            }
    
            $zipArch->close();
    
            return $toFilePath;
    
        }
    
    
    
    
        /**
    
         * 压缩文件夹
    
         * @param string $dirPath 要压缩的文件夹路径
    
         * @param string $toFilePath 压缩文件路径
    
         * @param bool $includeSelf 是否包含自身
    
         * @return string
    
         * @throws Exception
    
         */
    
        public static function packageDir(string $dirPath, string $toFilePath, bool $includeSelf = true)
    
        {
    
            if (!is_dir($dirPath)) throw new Exception('文件夹不存在。');
    
            $toFilePathInfo = pathinfo($toFilePath);
    
            if (!is_dir($toFilePathInfo['dirname'])) {
    
                mkdir($toFilePathInfo['dirname'], 0777, true);
    
            }
    
    
    
    
            $dirPathInfo = pathinfo($dirPath);
    
            //print_r($dirPathInfo);
    
    
    
    
            $zipArch = new ipArchive();
    
            if ($zipArch->open($toFilePath, ipArchive::CREATE) !== true) throw new Exception('压缩失败。');
    
    
    
    
            $dirPath = rtrim($dirPath, '/') . '/';
    
            $filePaths = self::scanDir($dirPath);
    
            if ($includeSelf) {
    
                array_unshift($filePaths, $dirPath);
    
            }
    
            //print_r($filePaths);
    
            foreach ($filePaths as $filePath) {
    
                $localName = mb_substr($filePath, mb_strlen($dirPath) - ($includeSelf ? mb_strlen($dirPathInfo['basename']) + 1 : 0));
    
                //echo $localName . PHP_EOL;
    
    
    
    
                if (is_dir($filePath)) {
    
                    $zipArch->addEmptyDir($localName);
    
                } else if (is_file($filePath)) {
    
                    $zipArch->addFile($filePath, $localName);
    
                }
    
            }
    
            $zipArch->close();
    
            return $toFilePath;
    
        }
    
    
    
    
        /**
    
         * 压缩单个文件
    
         * @param string $filePath 要压缩的文件路径
    
         * @param string $toFilePath 压缩文件路径
    
         * @return string
    
         * @throws Exception
    
         */
    
        public static function packageFile(string $filePath, string $toFilePath)
    
        {
    
            if (!is_file($filePath)) throw new Exception('文件不存在。');
    
            $toFilePathInfo = pathinfo($toFilePath);
    
            if (!is_dir($toFilePathInfo['dirname'])) {
    
                mkdir($toFilePathInfo['dirname'], 0777, true);
    
            }
    
            $filePathInfo = pathinfo($filePath);
    
            $zipArch = new ipArchive();
    
            if ($zipArch->open($toFilePath, ipArchive::CREATE) !== true) throw new Exception('压缩失败。');
    
            $zipArch->addFile($filePath, $filePathInfo['basename']);
    
            $zipArch->close();
    
            return $toFilePath;
    
        }
    
    
    
    
        /**
    
         * 字符串转为UTF8字符集
    
         * @param string $str
    
         * @return false|string
    
         */
    
        private static function convertToUtf8(string $str)
    
        {
    
            $charset = mb_detect_encoding($str, ['UTF-8', 'GBK', 'BIG5', 'CP936']);
    
            if ($charset !== 'UTF-8') {
    
                $str = iconv($charset, 'UTF-8', $str);
    
            }
    
            return $str;
    
        }
    
    
    
    
     /**
    
         * 删除目录以及子目录等所有文件
    
         *
    
         * - 请注意不要删除重要目录!
    
         *
    
         * @param string $path 需要删除目录路径
    
         * @param bool $delSelf 是否删除自身
    
         */
    
        private static function deleteDir(string $path, bool $delSelf = true)
    
        {
    
            if (!is_dir($path)) {
    
                return;
    
            }
    
            $dir = opendir($path);
    
            while (false !== ($file = readdir($dir))) {
    
                if (($file != '.') && ($file != '..')) {
    
                    $full = $path . '/' . $file;
    
                    if (is_dir($full)) {
    
                        self::deleteDir($full, true);
    
                    } else {
    
                        unlink($full);
    
                    }
    
                }
    
            }
    
            closedir($dir);
    
            if ($delSelf) {
    
                rmdir($path);
    
            }
    
        }
    
    
    
    
        /**
    
         * 遍历文件夹,返回文件路径列表
    
         * @param string $path
    
         * @param string $fileType all|file|folder
    
         * @param bool $traversalChildren 是否遍历下级目录
    
         * @return array
    
         */
    
        private static function scanDir(string $path, string $fileType = 'all', bool $traversalChildren = true)
    
        {
    
            if (!is_dir($path) || !in_array($fileType, ['all', 'file', 'folder'])) {
    
                return [];
    
            }
    
            $path = rtrim($path, '/');
    
            $list = [];
    
            $files = scandir($path);
    
            foreach ($files as $file) {
    
                if ($file != '.' && $file != '..') {
    
                    $p = $path . '/' . $file;
    
                    $isDir = is_dir($p);
    
                    if ($isDir) {
    
                        $p .= '/';
    
                    }
    
                    if ($fileType === 'all' || ($fileType === 'file' && !$isDir) || ($fileType === 'folder' && $isDir)) {
    
                        $list[] = $p;
    
                    }
    
                    if ($traversalChildren && $isDir) {
    
                        $list = array_merge($list, self::scanDir($p, $fileType, $traversalChildren));
    
                    }
    
                }
    
            }
    
            return $list;
    
        }
    
    }
    
     

     

    调用:

    //示例1:解压zip文件到/test/test1/目录下
    
    ZipUtil::extract('/test/test1.zip', '/test/test1/');
    
    
    
    
    //示例2:解压rar文件到/test/test2/目录下
    
    ZipUtil::extract('/test/test2.rar', '/test/test2/');
    
    
    
    
    //示例3:解压phar文件到/test/test3/目录下
    
    ZipUtil::extract('/test/test3.phar', '/test/test3/');
    
    
    
    
    //示例4:解压tar文件到/test/test4/目录下
    
    ZipUtil::extract('/test/test4.tar', '/test/test4/');
    
    
    
    
    //示例5:解压gz文件到/test/test5/目录下
    
    ZipUtil::extract('/test/test5.tar.gz', '/test/test5/');
    
    
    
    
    //示例6:解压bz2文件到/test/test6/目录下
    
    ZipUtil::extract('/test/test6.tar.bz2', '/test/test6/');
    
    
    
    
    //示例7:解压7z文件到/test/test7/目录下
    
    ZipUtil::extract('/test/test7.7z', '/test/test7/');
    
    
    
    
    //示例8:压缩单个文件
    
    ZipUtil::packageFile('/test/test8/1.txt', '/test/test8.zip');
    
    
    
    
    //示例9:压缩多个文件
    
    ZipUtil::package([
    
     ['file_type'=>'file', 'file_path'=>'/test/test9/1.txt', 'local_name'=>'1.txt'],
    
     ['file_type'=>'file', 'file_path'=>'/test/test9/2.txt', 'local_name'=>'2.txt'],
    
     ['file_type'=>'folder', 'local_name'=>'1/'],
    
     ['file_type'=>'folder', 'local_name'=>'2/'],
    
    ], '/test/test9.zip');
    
    
    
    
    //示例10:压缩文件夹
    
    ZipUtil::packageDir('/test/test10/', '/test/test10.zip');
    
     

    五、 结语

    在刚开始使用这个工具类的过程中,发现在了个坑,就是在window压缩的zip文件放到linux进行解压会发生文件名乱码的情况,所以不能直接使用extractTo方法进行解压,需要对zip解压出来的文件名进行转码。

  • 相关阅读:
    vue-实践1
    node 基本使用
    vue通信
    初始AMD和Common.js
    vue正确引入第三方包
    常见的java设计模式
    springboot加ES实现全局检索
    Cookie丢失的原因
    动态SQL
    用Java实现给图片添加文字
  • 原文地址:https://www.cnblogs.com/eflypro/p/14363460.html
Copyright © 2020-2023  润新知