AWS SDK for PHP v3 の ObjectUploader の使い方としきい値

PHPAWS

AWS SDK for PHP にはファイルアップロードのための ObjectUploader メソッドがあって、ファイルサイズによって最適なアップロード方法で処理してくれる。

自分が携わっている開発中の Web アプリでは、 ( S3 へ) 小さなファイルも大きなサイズのファイルもアップロードされるので、 ObjectUploader を使ってみた。また、アップロード方法が切り替わる境界となるファイルサイズ (しきい値) を調べた。

スポンサーリンク

ObjectUploader とは

AWS SDK for PHP には、 5 GB までのファイルをアップロードできる PutObject と、大容量ファイルをアップロードするための MultipartUploader メソッドが用意されている。

ObjectUploader は、アップロードされたファイルサイズによって、 PutObjectMultipartUploader のどっちを使うか選んで処理してくれるというもの。

PutObject または MultipartUploader がタスクに最適かどうかが不明な場合は、ObjectUploader を使用します。ObjectUploader は、ペイロードサイズに基づいてどれが最適かにより、PutObject または MultipartUploader を使用して大きなファイルを Amazon S3 にアップロードします。
AWS SDK for PHP バージョン 3 での Amazon S3 マルチパートアップロードの使用 より

PutObject は 5 GB までのファイルが扱えるとあるが、公式ドキュメントには以下の記述があるため、 100 MB 以上のファイルがアップロードされるなら ObjectUploader を使った方がいいと判断した。
(大きいサイズのファイルしかアップロードされないのであれば、 MultipartUploader だけ使えばいいと思う。)

Amazon S3 のユーザーには、100 MB を超えるオブジェクトに対してマルチパートアップロードを使用することをお勧めします。

ObjectUploader でファイルアップロードする方法

アップロード処理は ファイルアップロードの例外処理はこれぐらいしないと気が済まない を参照。

コード

require 'vendor/autoload.php';
use Aws\S3\Exception\S3Exception;
use Aws\S3\S3Client;
use Aws\S3\ObjectUploader;

$s3Client = new S3Client([
  'version' => 'latest',
  'region'  => 'your-region'
]);
$bucket = 'your-bucket';

try {

    if (!isset($_FILES['upfile']['error']) || !is_int($_FILES['upfile']['error'])) {
        throw new RuntimeException('パラメータが不正です');
    }

    switch ($_FILES['upfile']['error']) {
        case UPLOAD_ERR_OK:
            break;
        case UPLOAD_ERR_NO_FILE:
            throw new RuntimeException('ファイルが選択されていません');
        case UPLOAD_ERR_INI_SIZE:
        case UPLOAD_ERR_FORM_SIZE:
            throw new RuntimeException('ファイルサイズが大きすぎます');
        default:
            throw new RuntimeException('その他のエラーが発生しました');
    }

    // 1 GB まで
    if ($_FILES['upfile']['size'] > 1073741825) {
        throw new RuntimeException('ファイルサイズが大きすぎます');
    }

    $filename = $file['upfile']['name'];
    $minetype = mime_content_type($_FILES['upfile']['tmp_name']);
    // Using stream instead of file path
    $source = fopen($_FILES['upfile']['tmp_name'], 'rb');

    try {
        $uploader = new ObjectUploader(
            $s3Client,
            $bucket,
            $filename,
            $source,
             'private',
            array('params' => array('ContentType' => $minetype))
        );

        do {
            try {
                $result = $uploader->upload();
                if ($result["@metadata"]["statusCode"] == '200') {
                  print('<p>ファイルアップロード成功. アップロード先 : ' . $result["ObjectURL"] . '.</p>');
                }

            } catch (MultipartUploadException $e) {
                rewind($source);
                $uploader = new MultipartUploader($s3Client, $source, [
                    'state' => $e->getState(),
                ]);
            }
        } while (!isset($result));

    } catch (S3Exception $e) {
        echo $e->getMessage();
    }

} catch (RuntimeException $e) {
    echo $e->getMessage();
}

解説

ObjectUploader() の引数に渡すもの

第一引数 : S3 インスタンス
第二引数 : バケット名
第三引数 : オブジェクトキー名(ファイル名)
第四引数 : ファイルデータ
第五引数 : アクセスコントロールリスト ( ACL ) [デフォルト: private ] 省略可
第六引数 : オプション(設定値) 省略可

ここでは、第六引数で、 Content-Type を指定している。
Content-Type を指定しないでファイルアップロードすると、 Content-Typeapplication/octet-stream となっていた。
( Content-Typee は、 AWS S3 管理画面から対象のオブジェクト画面にいき、「プロパティ」→「メタデータ」で確認できる。)

Content-Type を指定した場合、
xlsx ファイルだと、 Content-Typeapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet
docx は、 application/vnd.openxmlformats-officedocument.wordprocessingml.document
となっていた。

しきい値( Threshold )について

ObjectUploader を使った場合、どのくらいのファイルサイズなら MultipartUploader が選択されるのか?

デフォルトでは 16777216 Byte ( 16 MB )がしきい値になっている。
( 100 MB 以上ならマルチパートアップロード推奨だけど、ここでの閾値は全然小さいんだな。。。)

const DEFAULT_MULTIPART_THRESHOLD = 16777216;
ObjectUploader.php

ObjectUploader.php を少しいじって、 15 MB と 16 MB のファイルをアップロードして PutObjectMultipartUploader のどちらが使われるのか確かめた。

アップロードファイル作成 ( Mac )

$ mkfile 15m test15m.txt
$ mkfile 16m test16m.txt

aws-sdk-php/src/S3/ObjectUploader.php
echo させてみた。

public function promise()
{
    /** @var int $mup_threshold */
    $mup_threshold = $this->options['mup_threshold'];
    if ($this->requiresMultipart($this->body, $mup_threshold)) {
      // ココ
      echo "<br>Perform a multipart upload. size:".$this->body->getSize();
        // Perform a multipart upload.
        return (new MultipartUploader($this->client, $this->body, [
                'bucket' => $this->bucket,
                'key'    => $this->key,
                'acl'    => $this->acl
            ] + $this->options))->promise();
    }

    // Perform a regular PutObject operation.
    $command = $this->client->getCommand('PutObject', [
            'Bucket' => $this->bucket,
            'Key'    => $this->key,
            'Body'   => $this->body,
            'ACL'    => $this->acl,
        ] + $this->options['params']);
    if (is_callable($this->options['before_upload'])) {
        $this->options['before_upload']($command);
    }
    // ココ
    echo "<br>Perform a regular PutObject operation. size:".$this->body->getSize();
    return $this->client->executeAsync($command);
}

結果

test15m.txt をアップロード
Perform a regular PutObject operation. size:15728640

test16m.txt をアップロード
Perform a multipart upload. size:16777216

15MB のファイルは PutObject が使われ、 16 MB のファイルは MultipartUploader が使われた。

しきい値を変更

ObjectUploader のインスタンス作成時に、第六引数の $optionsmup_threshold パラメータでしきい値を指定できる。
しきい値を 31 MB ( 32505856 Byte) に設定して、 30 MB と 31 MB のファイルをアップロードする。

$uploader = new ObjectUploader(
    $s3Client,
    $bucket,
    $filename,
    $source,
     'private',
    array('mup_threshold' => 32505856, 'params' => array('ContentType' => $minetype))
);

結果

test30m.txt をアップロード
Perform a regular PutObject operation. size:31457280

test31m.txt をアップロード
Perform a multipart upload. size:32505856

しきい値が変更され、 31 MB 以上のファイルをアップロードすると、 MultipartUploader が使われる。

Posted by Agopeanuts