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('\ZipArchive')) throw new \Exception('未安裝Zip插件。');




                $zipArch = new \ZipArchive();

                if ($zipArch->open($zipFilePath) !== true) throw new \Exception('解壓失敗。');




                //$zipArch->extractTo($toDirPath); //這個中文會亂碼




                //解決中文會亂碼

                $fileNum = $zipArch->numFiles;

                for ($i = 0; $i < $fileNum; ++$i) {

                    $statInfo = $zipArch->statIndex($i, \ZipArchive::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 \ZipArchive();

        if ($zipArch->open($toFilePath, \ZipArchive::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 \ZipArchive();

        if ($zipArch->open($toFilePath, \ZipArchive::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 \ZipArchive();

        if ($zipArch->open($toFilePath, \ZipArchive::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解壓出來的文件名進行轉碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章