思いがけず発生して時間を取られたので、備忘録。
解凍されたファイル数とZipArchive::numFiles()が一致しない
1つのファイルを圧縮したzipファイルに対し、PHPでZipArchive::numFiles()を実行すると、1ではなく2が返される場合があります。
以下のようにすれば、zipファイル内の各エントリ名を確認できるので原因がわかります。
$zip = new ZipArchive();
if ($zip->open('hoge.zip') === TRUE) {
echo "Total entries: " . $zip->numFiles . "\n";
for ($i = 0; $i < $zip->numFiles; $i++) {
echo "[$i] " . $zip->getNameIndex($i) . "\n";
}
$zip->close();
} else {
echo "Failed to open zip file.";
}
1つのファイルなのに2になってしまう原因は、以下の2つがあります。
1. ディレクトリが含まれている
archive.zip
└── folder/
└── data.csv
この場合、ディレクトリも1とカウントするので2になります。
ディレクトリが含まれる場合は除外し、ファイルだけを対象にするには以下のようにすれば良いです。
for ($i = 0; $i < $zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
if (substr($name, -1) !== '/') { // ディレクトリでない(スラッシュで終わっていない)
echo "file: $name\n";
}
}
ディレクトリエントリが入る理由
- 多くの圧縮ソフト(macOS標準、Windowsエクスプローラー、
zip
コマンドなど)は、フォルダ構造をそのままzipに含めるため、ディレクトリもエントリとして登録される ZipArchive::numFiles()
は「すべてのエントリ数」を返すため、「ファイル」以外も含まれる
2. __MACOSX/._ファイル が含まれている
macOSではターミナルやFinderなどから『”hoge.txt”を圧縮』してzip圧縮ファイルを作成できますが、このとき自動的にApple独自の拡張情報である『__MACOSX/._hoge.txt』が生成されて圧縮ファイルに追加されます。
このため、ZipArchive::numFiles() のカウントも増えます。
__MACOSX/._ファイルとは?
- AppleDouble 形式と呼ばれる、macOS独自のメタデータ保存用ファイル
- 通常ファイルと対になる
.DS_Store
や._xxxx
形式のファイルは、ファイルのFinderタグやアイコン位置などを保存している - Macでは見えなくても、他のOSやZipArchiveなどでは「普通のファイル」として扱われる
__MACOSX ディレクトリおよび . 系ファイルが含まれている場合に除外するには、以下のようにすれば良いです。
for ($i = 0; $i < $zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
// __MACOSX ディレクトリおよび ._ 系ファイルを除外
if (strpos($name, '__MACOSX/') === 0 || strpos(basename($name), '._') === 0) {
continue;
}
echo "Valid file: $name\n";
}
zip作成するときApple拡張を生成しないようにする
macOSでzipを作成するとき、Finderではなくターミナルのzipコマンドを使い、Apple拡張を無効にする『-X』オプションを指定すれば良いです。
zip -r -X archive.zip hoge.txt
-X
オプションは “exclude extra file attributes” の意味で、これにより__MACOSX
や._
ファイルの生成を抑止します。
まとめ
zipファイルにディレクトリやmacOSで生成されるApple拡張が含まれる場合、PHPのZipArchive::numFiles()とファイル数が一致しません。
あまり遭遇しないとは思いますが、何かの参考になれば嬉しいです。
PHPの勉強には、以下の書籍がとても参考になります。
詳細 : 著者: 小川 雄大 / 出版社: 技術評論社 / ページ数: 592ページ / 発売日: 2010/11/12
テキストよりも動画で集中して学びたい方はこちら。