作為壓縮解壓縮的擴展學習,兩大王牌壓縮格式rar 和zip 一直是電腦領域的壓縮終結者。 rar 格式的壓縮包是Windows 系統中有接近統治地位的存在,今天我們學習的PHP 擴充就是針對於rar 的壓縮包操作,不過,PHP 的rar 擴充只能讀取和解壓rar 格式的壓縮包,並不能進行壓縮操作。
php-rar 擴充功能在 pecl 的安裝套件已經過時了,無法在 PHP7 中使用,我們需要使用它在 github 上的原始碼進行編譯安裝才能夠在 PHP7 的環境下安裝成功。
https://github.com/cataphract/php-rar
直接 git clone 之後就可以用正常的 PHP 擴充功能的方式進行安裝。
取得壓縮套件句柄 RarArchive
$arch = RarArchive::open("test.rar"); $archNo = rar_open("test.rar"); echo $arch, PHP_EOL; // RAR Archive "/data/www/blog/test.rar" echo $archNo, PHP_EOL; // RAR Archive "/data/www/blog/test.rar" $arch->close(); rar_close($archNo); echo $arch, PHP_EOL; // RAR Archive "/data/www/blog/test.rar" (closed) echo $archNo, PHP_EOL; // RAR Archive "/data/www/blog/test.rar" (closed)
php-rar 擴充有兩種形式的寫法,一種是物件導向的,也就是使用 RarArchive 類別來操作壓縮套件。另一種方式就是直接使用一個函數 rar_open 用來取得一個 rar 檔案的句柄。它們都重寫了 __toString 方法,所以我們可以直接列印句柄的內容看到目前句柄所操作的特定檔案。
當我們關閉句柄時,句柄物件依然能夠進行輸出,但後面會顯示一個 closed 。這時的句柄物件已經不能進行其它操作了。
$arch = RarArchive::open("test.rar"); $archNo = rar_open("test.rar"); echo $arch->getComment(), PHP_EOL; echo $arch->isBroken(), PHP_EOL; echo $arch->isSolid(), PHP_EOL; echo rar_comment_get($archNo), PHP_EOL; echo rar_broken_is($archNo), PHP_EOL; echo rar_solid_is($archNo), PHP_EOL; echo $arch->setAllowBroken(true), PHP_EOL; echo rar_allow_broken_set($archNo, true), PHP_EOL;
RarArchive 物件的一些方法可以幫助我們取得目前壓縮套件的資訊。例如 getComment() 取得壓縮包的說明訊息,isBroken() 取得目前壓縮包是否有損壞,isSolid() 檢查目前壓縮包是否可用。而 setAllowBroken() 方法是讓我們允許對損壞的壓縮包進行操作。這裡我們給了物件導向和過程導向的寫法。
壓縮套件內的每個實體檔案或目錄作業 RarEntry
取得壓縮套件的句柄之後,我們就需要更進一步取得壓縮套件內部的內容。而句柄物件中就已經保存了壓縮包內部的各個檔案和目錄的物件 RarEntry 。
$gameEntry = $arch->getEntry('ldxlcs/ldxlcs/game.htm'); echo $gameEntry->getName(), PHP_EOL; // ldxlcs/ldxlcs/game.htm echo $gameEntry->getUnpackedSize(), PHP_EOL; // 56063 $gameEntryNo = rar_entry_get($arch, "ldxlcs/ldxlcs/game.htm"); echo $gameEntry->getName(), PHP_EOL; // ldxlcs/ldxlcs/game.htm echo $gameEntry->getUnpackedSize(), PHP_EOL; // 56063 $fp = $gameEntryNo->getStream(); while (!feof($fp)) { $buff = fread($fp, 8192); if ($buff !== false) { echo $buff; } else { break; } //fread error } // 输出文件的全部内容 echo PHP_EOL; echo 'Entry extract: ', $gameEntry->extract("./"), PHP_EOL;
句柄物件的 getEntry() 方法就是用來取得指定的檔案或目錄內容的。它取得的是單一文件或目錄,所以必須明確指定需要取得的文件內容。透過這個方法,我們可以拿到一個 RarEntry 物件。接下來,就是這個物件的一些操作。
RarEntry 物件的 getName() 方法用來取得檔案名稱,這個檔案名稱是帶路徑的,這個路徑是壓縮套件內的絕對路徑。 getUnpackedSize() 方法用來取得檔案的大小,getStream() 用來取得檔案流,透過 getStream() 方法,我們就可以直接列印輸出檔案的內容。
當然,最重要的是,我們可以透過 extract() 方法來直接解壓縮一個檔案到指定的目錄。 php-rar 擴充功能並沒有提供一個能夠完全解壓縮整個壓縮包的方法,所以如果我們需要對整個壓縮包進行解壓縮的話,就需要通過循環遍歷壓縮包內部的全部內容來對這些文件一個一個地進行解壓。
最後,我們就來看看如何遍歷壓縮包內的全部內容。
$entries = $arch->getEntries(); foreach ($entries as $en) { echo $en, PHP_EOL; echo $en->getName(), PHP_EOL; echo $en->getUnpackedSize(), PHP_EOL; echo $en->getAttr(), PHP_EOL; echo $en->getCrc(), PHP_EOL; echo $en->getFileTime(), PHP_EOL; echo $en->getHostOs(), PHP_EOL; echo $en->getMethod(), PHP_EOL; echo $en->getPackedSize(), PHP_EOL; echo $en->getVersion(), PHP_EOL; echo $en->isDirectory(), PHP_EOL; echo $en->isEncrypted(), PHP_EOL; } // 压缩包中所有文件的内容 // RarEntry for file "ldxlcs/ldxlcs/game.htm" (3c19abf6) // ldxlcs/ldxlcs/game.htm // 56063 // 32 // 3c19abf6 // 2017-09-10 13:25:04 // 2 // 51 // 7049 // 200 // …… $entriesNo = rar_list($archNo); foreach ($entriesNo as $en) { echo $en->getName(), PHP_EOL; }
直接使用的是 RarArchive 物件的 getEntries() 方法,我們透過這個方法可以得到一個 RarEntry 物件的陣列,裡麵包含的就是這個 rar 壓縮套件裡面的全部內容。在這段程式碼中,我們也列印了 RarEntry 物件的其它一些屬性方法,根據名稱也能大概了解這些方法都是獲取關於文件的各種資訊的,大家可以自行測試。
異常處理
最後,如果開啟錯了檔案或取得壓縮套件內部沒有的檔案時,php-rar 擴充功能會以 PHP 錯誤的形式報錯。但既然提供了完整的物件導向寫法,那麼它也必然提供了一套物件導向的異常處理機制。
// 不打开 UsingExceptions 全部错误会走 PHP 错误机制,打开后走 PHP 的异常机制 RarException::setUsingExceptions(true); var_dump(RarException::isUsingExceptions()); // bool(true) try { $arch = RarArchive::open("test1.rar"); $arch->getEntry('ttt.txt'); } catch (RarException $e) { var_dump($e); // object(RarException)#35 (7) { // ["message":protected]=> // string(91) "unRAR internal error: Failed to open /data/www/blog/test1.rar: ERAR_EOPEN (file open error)" // ["string":"Exception":private]=> // string(0) "" // ["code":protected]=> // int(15) // ["file":protected]=> // string(22) "/data/www/blog/rar.php" // ["line":protected]=> // int(93) // ["trace":"Exception":private]=> // array(1) { // [0]=> // array(6) { // ["file"]=> // string(22) "/data/www/blog/rar.php" // ["line"]=> // int(93) // ["function"]=> // string(4) "open" // ["class"]=> // string(10) "RarArchive" // ["type"]=> // string(2) "::" // ["args"]=> // array(1) { // [0]=> // string(9) "test1.rar" // } // } // } // ["previous":"Exception":private]=> // NULL // } }
只要將RarException::setUsingExceptions() 設為true ,就能夠開啟php-rar 擴展的異常處理機制,這時,我們打開一個錯誤的文件,或者去獲取壓縮包內的一個錯誤文件路徑,那麼,錯誤訊息就會以異常的形式進行拋出。
總結
這套擴充是不是感覺很人性化?即提供了物件導向的方式,也提供了以函數操作為主的面向過程的方式。但是,這樣做其實並沒有太多的好處,因為又要兼顧老程式碼,又要兼顧新思想,本身擴展的內部實作相必也會複雜很多。我們自己寫程式碼的時候就盡量不要這麼寫了,在重構的時候一步步的向最新的形式遷移即可。
关于 rar 的压缩操作并没有找到太多有用的资料。当然,我们在生产环境中如果要生成压缩包的话大部分情况下都会直接去生成 zip 格式的提供给用户,毕竟大部分的客户端软件都是能够同时支持 rar 和 zip 格式文件的解压的,如果一定要指定生成 rar 的话,也可以多多和产品经理或者客户商量。有的时候,技术的难点是可以通过业务的变通来解决的,最重要的其实还是在于沟通。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202007/source/PHP%E7%9A%84rar%E8%A7%A3%E5%8E%8B%E8%AF%BB%E5%8F%96%E6%89%A9%E5%B1%95%E5%8C%85%E5%AD%A6%E4%B9%A0.php
推荐:《PHP视频教程》