[php] zipファイルを作成/ダウンロードする際に気をつけること

バックアップのために、phpでzipファイルを作成してダウンロードさせるプログラムを書いたのですが、ありがちな引っかかるポイントがあったのでメモしておきます。

書いたコード

書いたコードは以下です。
zipにする処理はlinuxのzipコマンドを使いました。

// ターゲットディレクトリの中身をクリア
$command = "rm -f ".$targetDir."*";
exec($command);

// ターゲットディレクトリに日付のディレクトリを作成
$dateDir = $targetDir."/".date("Ymd");
mkdir($dateDir,0777);

// zipでまとめたいファイルを日付ディレクトリの中にコピー
copy($src,$dateDir.$dest);

// カレントディレクトリを記録しておく
$basedir = getcwd();
// ターゲットディレクトリに移動
chdir($targetDir);
// backup.zipという名前で作成
$command = "zip -r backup ./*";
exec($command);

// ダウンロード処理
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($zippath));
header('Content-Disposition: attachment; filename="'.basename($zippath).'"');
readfile($zippath);

どこが問題だったか

結論から言うと、ファイルをクリアする際に再帰的に削除していなかったことと、ディレクトリ作成時に存在確認をしていないのが問題でした。

エラーの内容

このコード、間違いはほとんどないです。
厄介なポイントがいくつかあります。

  • zipにするのがファイルのみだったら問題なく動作する。
  • zipにしたい内容にディレクトリが含まれるとき、エラーが発生する
    (上記ではターゲットディレクトリの中に日付のディレクトリを作成)
  • 最初の一回は問題なく動作する。二回目から失敗する。
  • 二回目以降、失敗はするが、zipファイルはダウンロードできる。
  • ダウンロードしたzipファイルを解凍すると、ほとんどのファイルは見られるが、一部のファイルが見られないような状態に陥る。
    (必ずそうなるかはわかりません)

気づいたこと

ターゲットディレクトリの中にzipファイルを作成していたのですが、そのzipファイルをサーバから直接ダウンロードすると、エラー無く解凍できることがわかりました。
また、php経由でダウンロードしたzipファイルと、サーバから直接ダウンロードしたzipファイルでは、php経由のほうが1KBほど大きいことに気づきました。

エラーの原因

上述の通り、 ファイルをクリアする際に再帰的に削除していなかったため、ディレクトリ作成時にphpが「既にディレクトリが存在する」というエラーを吐いていました。
そのエラー文字列がheader出力前に表示されていたことで、エラー文字列もまとめてzipの一部と解釈され、zipファイルにゴミが紛れ込んでいた状態に陥ってしまったというわけです。
修正したのは下記。

// ターゲットディレクトリの中身をクリア
$command = "rm -rf ".$targetDir."*";   // <=再帰的に削除
exec($command);

// ターゲットディレクトリに日付のディレクトリを作成
$dateDir = $targetDir."/".date("Ymd");
if(!file_exists($dateDir)){            // <= ディレクトリの存在確認
  mkdir($dateDir,0777);
}

まとめ

ぶっちゃけ、めっちゃよくあることなんですが、割とどうでもいいことでもブログに書いていこうかなという気持ちになったので、書いてみました。
誰かの参考になれば幸いです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です