[PHP]AWS S3から複数ファイルをzipダウンロードする

2019-10-05PHPAWS

AWS SDK for PHP バージョン 3 で AWS S3 から複数のファイルを ZIP にしてダウンロードする方法。
PHP の拡張ライブラリ ZipArchive を使っても ZIP ダウンロードはできるけれど、 maennchen/ZipStream-PHP というライブラリを使うと簡単に実装できるので、 maennchen/ZipStream-PHP の使い方。

ZipStream-PHP とは

PHP の ZIP ストリーミングライブラリ。
maennchen/ZipStream-PHP

バージョン 1.0.0 以上は PHP 7.1 以降が必須。
バージョン 0.5.2 は PHP 7.0 以降が必須。

おすすめの理由

ストリーミングでファイルを読み込み、圧縮して、ダウンロードさせるとこまでやってくれる。
ストリーミングなので、サーバーに一時ファイルが生成されない。メモリを大量に消費しない。
「 S3 対応」と謳っている。

ZipStream-PHP の使い方

PHP 7.0 の環境しかなかったため、バージョン 0.5.2 の使い方。

バージョン 1 系とは options の指定の仕方が異なる。

All options parameters to all function have been moved from an array to structured option objects.

composer でインストール

$ composer require maennchen/zipstream-php

S3 から複数ファイルをダウンロードするサンプルコード

// ライブラリをロードする
require 'vendor/autoload.php';
use Aws\S3\Exception\S3Exception;
use Aws\S3\S3Client;
$bucket = "YOUR-BUCKET-NAME";
// S3 インスタンスの生成
$s3Client = new S3Client([
    'version' => 'latest',
    'region'  => 'YOUR-REGION'
]);
// Amazon S3 ストリームラッパーを登録
$s3Client->registerStreamWrapper();
// 画面から送られてきたファイル名のリスト(チェックボックスでダウンロードしたいファイル名を選択)
$keyList = filter_input(INPUT_POST, 'hoge', FILTER_DEFAULT,FILTER_REQUIRE_ARRAY);
// ZIP ファイル名
$zipName = 'ZIP-FILE-NAME.zip';
// オプション設定
$options = array(
             'content_type' => 'application/zip',
             'comment' => 'this is a TEST'
            );
// ZipStream インスタンス生成
$zip = new ZipStream\ZipStream($zipName, $options);
foreach ($fileList as $key) {
  $file = "s3://" . $bucket . "/" . $key;
  //addFileFromStream
  if ($streamRead = fopen($file, 'r')) {
    $zip->addFileFromStream($key, $streamRead);
    fclose($streamRead);
  } else {
    throw new RuntimeException('Could not open stream for reading');
  }
}
// ZIP ストリームを閉じる
$zip->finish();
説明

ZipStream インスタンス生成時に、第一引数に ZIP ファイル名と、第二引数にオプション設定の配列を渡す。 第二引数は省略可。

addFileFromStream() の第一引数が、 ZIP の中のファイル名となる。ディレクトリを含めてもいい。

Content_Type に指定される MIME タイプのデフォルトは 'application/x-zip' になっている。
$options で、 HTTP ヘッダの 'Content-Type''Content-Disposition' 、コメントなどを指定できる。

HTTP ヘッダの設定値
$headers = array(
    'Content-Type' => $content_type,
    'Content-Disposition' => $disposition,
    'Pragma' => 'public',
    'Cache-Control' => 'public, must-revalidate',
    'Content-Transfer-Encoding' => 'binary'
);
$disposition はこうなってる。
$disposition .= "; filename=\"{$this->output_name}\"";

ZIP ファイル名が日本語だと IE や Edge では文字化けする。。。

日本語ファイル名を使えるようにする ( IE , Edge 対応)

IE , Edge で日本語ファイル名を扱うには、 HTTP ヘッダで Content-Disposition: attachment; filename*=UTF-8''URLエンコードしたファイル名 を送る。

header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($filename));

だがしかし、下記のコードとなっているため、オプションで filename の後に *=UTF-8 とかを追加できない。

ZipStream.php ( v0.5.2 )
$disposition = 'attachment';
if (isset($opt[self::OPTION_CONTENT_DISPOSITION])) {
    $disposition = $opt[self::OPTION_CONTENT_DISPOSITION];
}
if ($this->output_name) {
    $disposition .= "; filename=\"{$this->output_name}\"";
}

なので、 vendor/maennchen/zipstream-php/src/ZipStream.php を修正した。 569 行あたり

if ($this->output_name) {
  //$disposition .= "; filename=\"{$this->output_name}\"";
  $urlencoded = rawurlencode($this->output_name);
  $disposition .= "; filename*=UTF-8''{$urlencoded}";
}

Posted by Agopeanuts