PHPでディレクトリを丸ごとZIP圧縮する方法の速度比較
Webサーバー上のcronジョブ等で、定期的にデータをZIP圧縮して日別アーカイブしたり、それらをダウンロードする処理ってWeb運営者のみなさんなら普通に実施してると思うんですね。
ぼくも当然のようにMySQLやPostgreSQLのDUMPデータをZIP圧縮したり、SQLiteだったらdbファイルをまんま圧縮したりしていますし、CMS等を使っているとimagesディレクトリ下の階層を保ったままでZIPアーカイブしたいという要望もあります。
そこでふと、PHPが持つZipArchiveクラスを使った場合と外部コマンドのzipを使った場合、どちらが早いのか気になったので実行してみました。
PHPでZipArchiveを使ってディレクトリを丸ごと圧縮する
PHPコード
function zip($path, $zipfile)
{
$za = new ZipArchive();
$za->open($zipfile, ZIPARCHIVE::CREATE);
zipSub($za, $path);
$za->close();
}
function zipSub($za, $path, $parentPath = '')
{
$dh = opendir($path);
while (($entry = readdir($dh)) !== false) {
if ($entry == '.' || $entry == '..') {
} else {
$localPath = $parentPath.$entry;
$fullpath = $path.'/'.$entry;
if (is_file($fullpath)) {
$za->addFile($fullpath, $localPath);
} else if (is_dir($fullpath)) {
$za->addEmptyDir($localPath);
zipSub($za, $fullpath, $localPath.'/');
}
}
}
closedir($dh);
}
使い方
zip('/public_html/images', '/backup/images.zip');
解説
PHP公式マニュアルにもサンプルが掲載されているので、あらためて解説するまでもない気もしますが、ZipArchiveクラスはファイルやディレクトリを追加する関数は持っていても、ディレクトリを指定するだけで丸ごと圧縮してくれるような便利な機能は持っていないので、自前でディレクトリを走査してあげる必要があります。
zipSubという関数を再帰表現で呼び出し、子階層をどんどん掘って、じゃんじゃんファイルを追加しているだけ。なんか、むかーしプログラミング初心者だった頃、解説本には再帰表現というのがなんだかすごく高等な技術のように書かれていて逆に頭が混乱した記憶があります。
自分の関数内で自分自身を呼び出す、だなんて書き方されたら混乱するだけだと思うんですけどねー。関数は関数。同じことをするのに何度も同じ関数を作るのはバカらしいから、パラメータを変えて呼び出しているに過ぎません。
このZipArchiveでの特殊な部分としては、そのまんまフルパス名を渡してしまうとZIPファイルの中にフルパスで記録されてしまうので、相対パスを指定するために$localPathを使ってごにょごにょやっている部分ですかね。
だって、例えば
/public_html/images/a.jpg /public_html/images/b.jpg /public_html/images/c.jpg /public_html/images/sub/a.jpg /public_html/images/sub/b.jpg /public_html/images/sub/c.jpg
みたいにファイルがあったとして、public_htmlというディレクトリ名から格納したくないじゃないですか。imagesの下だけ格納したいので、ZIPファイルの中は
a.jpg b.jpg c.jpg sub/a.jpg sub/b.jpg sub/c.jpg
こんなふうに格納されたほうがシンプルじゃありませんか? そのへんをごにょごにょやっているというわけです。
PHPでzipコマンドを使ってディレクトリを丸ごと圧縮する
function zip($path, $zipfile)
{
chdir($path);
exec("zip -r {$zipfile} .");
}
使い方
zip('/public_html/images', '/backup/images.zip');
解説
……………zipコマンドに -r オプションを渡しているだけですw
めっちゃシンプル!
工夫している点としては前述のZipArchiveの項と同様、パス名の部分です。
単に exec("zip -r {$zipfile} {$path}"); みたいな書き方をしてしまうとZIPファイル内にフルパスでファイルが格納されてしまいます。
これじゃ使いづらいので、いったんchdirで圧縮対象のディレクトリへ移動してから exec("zip -r {$zipfile} ."); というようにカレントディレクトリ以下を圧縮しなさいよ、という命令を実行しています。
なんで相対パスしか格納しないオプションとかないんだろうね。もしかしてぼくが知らないだけで、相対パス指定の方法あるのかな?
ともあれ、ぼくは知らないので、この方法で圧縮をしています。
ZipArchiveとzipコマンドの速度比較
環境はCore i3の非力なPCで、OSはWindows 10 Pro、Webサーバーはnginx、PHPは7.4を使用。
合計20MBくらいあるディレクトリを圧縮して速度計測
ZipArchive | 3.0794360637665sec |
zipコマンド | 1.4052131175995sec |
…………………………知ってた!w
そりゃあ、たいていの場合は外部コマンドのほうが早いよね。
zipコマンドだけじゃなく、画像圧縮だってGDライブラリ使うよりconvertコマンド使ったほうが高速だし。
でもまぁ、そうじゃないかなー?と何となくで想像しているより、しっかり自分でコード書いて確認できたので満足です。
今後もzipコマンドを使って定期的に自動メンテしようと思います。
まとめ
PHPでディレクトリを丸ごと圧縮するならZipArchive使うより、zipコマンド叩いたほうが早いよ!
たいていのレンタルサーバーでもzipコマンドくらいは普通使えるし。むしろ環境によってはZipArchiveクラスのほうが使えないケースが多いくらい。
尚、Windowsの場合はここからzip.exeをDLして使えばよろし。
http://gnuwin32.sourceforge.net/packages/zip.htm
以上、自分の知識欲を満たすためだけの記事でした。