[PHP] 複数ファイルをZipにしてダウンロートする2つの方法
複数のファイルをまとめて Zip に圧縮しダウンロードする。
1 つのフォルダに複数のファイルがあって、フォルダごと Zip にしてダウンロードする方法と、複数のフォルダにまたがった複数のファイルを Zip にしてダウンロードする方法。
方法 1 : フォルダごと Zip にしてダウンロード
例えば、 ClassA というフォルダに 3 つのファイルがあってこの 3 つのファイルを一括ダウンロードする場合、 フォルダ ClassA を 丸ごと Zip にする。
|-firstGrade |-ClassA |-サンジ.txt |-ゾロ.txt |-ルフィ.txt
PHP コード
// 丸ごと圧縮するフォルダ
$folder = "ClassA";
// Zip ファイル名
$fileName = $folder.".zip";
// ファイルディレクトリ
$dir = __DIR__."/firstGrade";
// Zip ファイルパス
$zipPath = $dir. '/' .$fileName;
$command = "cd ". $dir .";".
"zip -r '". $fileName . "' ./".$folder."/";
exec($command);
mb_http_output( "pass" ) ;
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: Binary");
header("Content-Length: ".filesize($zipPath));
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . $fileName);
ob_end_clean();
readfile($zipPath);
// Zipファイル削除
unlink( $zipPath );
exit;
これを実行すると、サーバー内の firstGrade フォルダの中に ClassA.zip が作成され、それをダウンロードする。最後に unlink
で作成された Zip ファイルは削除される。
コマンドの説明
cd
: ディレクトリの移動
zip
: ファイルを圧縮 , zip ZIPファイル名 対象フォルダ ( -r
オプションで再帰的にしないとでディレクトリだけが格納される)
exec
: Linux コマンドを実行
ダウンロード部分の説明
mb_http_output( "pass" );
のとこ
出力バッファの文字エンコーディングを変換する関数 mb_output_handler()
を無効にする。
mb_output_handler()
が呼び出されるとダウンロードファイルが壊れることがある。
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($fileName));
IE・Edge での日本語のファイル名文字化け対策
ob_end_clean();
readfile($zipPath);
出力バッファにゴミが残っているとダウンロードしたファイルが壊れることがあるので、 ob_end_clean()
を使って、出力バッファを破棄してから、ファイルの中身を出力させる。
* Content-Type
の application/force-download
は MIME タイプの(正式な)値ではない。
ブラウザは認識できない Content-Type
が指定されるとダウンロード処理になる(必ずではない)という動きを利用しているだけで、別に「 abc 」とかでもいい。
* SSL
環境になっていて、かつ IE8 以下の場合に、うまくいかないときは以下をつける。
header('Cache-Control: public');
header('Pragma: public');
方法 2 : ZipArchive クラスを使ってダウンロード
チェックボックスとかで選択されたファイルを圧縮してダウンロードしたい場合、またはフォルダを跨いでいてフォルダごと Zip にできないときは、 ZipArchive
クラスを使う。
ZipArchive
は、 PHP に Zip の拡張機能が入っていないと使えない。
ターミナルで、 $ php -m
コマンドを実行し「 zip 」がなければインストールする。
|-firstGrade |-ClassA |-サンジ.txt |-ゾロ.txt |-ルフィ.txt |-ClassB |-ウソップ.txt |-ナミ.txt |-ClassC |-チョッパー.txt |-ロビン.txt
チェックボックスでファイルを選択する画面
form
とかボタンは書いていない
<div class="box">
<?php
$dir = __DIR__."/firstGrade";
if (file_exists($dir)) {
$iterator = new RecursiveDirectoryIterator($dir);
$iterator = new RecursiveIteratorIterator($iterator);
$i=1;
foreach ($iterator as $key=>$fileinfo) {
if ($fileinfo->isFile()) {?>
<label><input type="checkbox" name="check[]" value="<?=$key?>"/>
<?=str_replace($dir."/","",$key)?></label><br>
<?php $i++;
}
}
} ?>
</div>
PHP
$chk = filter_input(INPUT_POST, 'check', FILTER_DEFAULT,FILTER_REQUIRE_ARRAY);
if(!empty($chk)) {
// Zip ファイル名
$fileName = "firstGrade.zip";
// ファイルディレクトリ
$dir = __DIR__."/firstGrade";
// Zip ファイルパス
$zipPath = $dir."/".$fileName;
// インスタンス作成
$zip = new ZipArchive();
// Zip ファイルをオープン
$res = $zip->open($zipPath, ZipArchive::CREATE);
// Zip ファイルのオープンに成功した場合
if ($res === true) {
foreach( $chk as $value ){
$newname = str_replace($dir."/","",$value);
// 圧縮するファイルを追加
$zip->addFile($value, $newname);
}
// Zip ファイルをクローズ
$zip->close();
mb_http_output( "pass" );
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: Binary");
header("Content-Length: ".filesize($zipPath));
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . $fileName);
ob_end_clean();
readfile($zipPath);
// zipを削除
unlink($zipPath);
}
}
これを実行すると、 firstGrade.zip の中は、選択した「フォルダ/ファイル」という構成になる。
ClassA/ルフィ.txt と ClassA/ゾロ.txt を選択してダウンロードすると、 firstGrade フォルダ – ClassA フォルダ - ルフィ.txt と ゾロ.txt が格納される。
ZipArchive
の説明
$zip->open($zipPath, ZipArchive::CREATE)
のとこ
open(パスを含めた Zip ファイル名 , オプション)
: Zip ファイルをオープンする。
オプション ZipArchive::CREATE
は、第一引数に指定した Zip ファイルを新規作成する。
ZipArchive::OVERWRITE
は Zip ファイルを上書きする。 ( PHP5.6 以上だと、第一引数に指定した Zip ファイルが存在しない場合、エラーとなる。)
オプションは繋げて書くことができる。
$zip->open($zipPath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)
$zip->addFile($value, $newname)
のとこ
addFile(追加するファイルのパス+ファイル名 , Zip の中でのファイル名)
: Zip にファイルを追加する。
第二引数は省略可。ただし省略すると、第一引数の「追加するファイルのパス」にある階層がそのまま Zip の中にも作られる。
このサンプルの場合、チェックボックスで取得するファイル名には、 __DIR__
も入っているため、 addFile
で第二引数を省略すると、ルートディレクトリから反映され階層が深くなる。
Zip にしたとき、 firstGrade 以下のディレクトリとファイルが欲しいので、 str_replace()
で不要部分を取り除いている。 ClassA とかのディレクトリも不要で、ファイルだけでいいなら、第二引数にはファイル名のみ指定する。