PHP(Laravel)のZipArchiveを使って、S3にある画像をzipダウンロードする。

# 実現したいこと
S3にある画像をまとめてダウンロードする。JavaScriptで無理やりやる方法もあるが、
ブラウザ側でアラートが発生するため、zipダウンロードを選択しました。

# 環境
- macOS Mojave 10.14.6
- Laravel6系
- PHP7.2

# 流れ
- S3を利用する設定をする。
- S3->サーバーに画像をダウンロード
- ZipArchiveを利用して画像をzipファイルにまとめる
- ダウンロードレスポンスとして、ブラウザに返す。
- 
※当初、ZipArchiveよりも少し簡単に利用できそうな、
Zipperというパッケージの利用を考えていましたが、Laravel6系に対応していませんでした。

# 実装
※現時点では、自身のMacbook上でのみ動作を確認していおります。

## S3を利用する設定をする。

envファイルに設定情報を記述します。

```:.env
AWS_ACCESS_KEY_ID=***
AWS_SECRET_ACCESS_KEY=***
AWS_DEFAULT_REGION=***
AWS_BUCKET=***
```
## 以下3つを一気に実装
- S3->サーバーに画像をダウンロード
- ZipArchiveを利用して画像をzipファイルにまとめる
- ダウンロードレスポンスとして、ブラウザに返す。

一気に処理を書いていきます。適宜、メソッドや別クラスに処理を分けてください。

```php:TestController.php

use ZipArchive;
use Aws\CommandPool;
use Aws\S3\S3Client;

(中略)

public function zipDownLoad(ZipArchive $zip)
{
    // バケット
    $bucket = config('filesystems.disks.s3.bucket');
    $imgDir = 'imgs';

    // 画像ファイル名
    $imgs = [
        'img1.jpg',
        'img2.jpg',
        'img3.jpg',
        'img4.jpg',
        'img5.jpg',
    ];

    // S3Client ※1
    $s3Client = new S3Client(
        [
            'version' => 'latest',
            'region' => config('filesystems.disks.s3.region');,
            'credentials' => [
                'secret' => config('filesystems.disks.s3.secret');,
                'key' => config('filesystems.disks.s3.key');,
            ],
        ]
    );

    // zipファイル名と保存先(あとで削除する)
    $zipFileName = 'test.zip';
    $zipFilePath = public_path() . '/' . $zipFileName;

    // レスポンスヘッダー
    $headers = ['Content-Type' => 'application/zip'];

    // 画像取得 ※2
    $commands = [];
    foreach ($imgs as $img) {
        $key = $imgDir . $img;
        $commands[] = $s3Client->getCommand(
            'GetObject',
            [
                'Bucket' => $bucket,
                'Key' => $key,
            ]
        );
    }

    $contents = CommandPool::batch($s3Client, $commands);

    // zipファイル作成 ※3
    $zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE);

    foreach ($contents as $i => $content) {
        // 取得できなかったらS3Exceptionが格納されてる。
        if (strpos(get_class($content), 'S3Exception') !== false) {
            continue;
        }

        $zip->addFromString($imgs[$i], $content['Body']);
    }

    $zip->close();

    // おまじない ※4
    ob_end_clean();

    // 返却
    return response()
            ->download($zipFilePath, $zipFileName, $headers) // ※5
            ->deleteFileAfterSend(true); // ※6
}
```

1. ※2のCommandPoolを使うため、Storageファサードではなく、S3Clientを生成しています。
2. S3に対してまとめてコマンド実行しています。[参考](https://qiita.com/endam/items/6bdcf4b1b86b337cc3b9)
3. ZipArchive::CREATEは「新規作成」、ZipArchive::OVERWRITEは「存在していたら上書き」という意味
4. この関数を実行しないとMacでは正しく解凍できない。[参考](http://soudan.hatenablog.jp/entry/php-zip-cpgz)
5. 引数は(ファイルパス, ダウンロードされるサイトのファイル名, レスポンスヘッダー)。第2,3引数は任意
6. レスポンス返却後にファイルを削除

# 参考サイト
- [S3へ一括でコマンドを実行したい場合はCommandPoolを使えという話](https://qiita.com/endam/items/6bdcf4b1b86b337cc3b9)
- [Laravelでzipファイルを作成する](https://reffect.co.jp/laravel/laravel-create-zip-file#Zipper)
- [phpでzip生成しダウンロードしたファイルをmacで展開するとcpgzになる](http://soudan.hatenablog.jp/entry/php-zip-cpgz)
- [Laravel では env() を config 系ファイル以外の場所に書いてはいけない](https://qiita.com/mpyw/items/34f37742d9a18b80a08c)
- [Laravel File Download](https://laravel.com/docs/6.x/responses#file-downloads)
- [ZipArchive クラス](https://www.php.net/manual/ja/class.ziparchive.php)