ちょっと話題の記事

AWS SDK for PHP v3のMultipart Upload機能を試してみた

2015.06.09

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

望月@シアトルです。24時間体制でブログを書くためにシアトルに飛ばされました。

少し前の話ですが、AWS SDK for PHPのVersion3が正式リリースされました。Version3の新機能の中で一つ目を引くものがありました。Amazon S3 Multipart Uploadsです。

Multipart Uploadを利用することで、巨大なファイルの分割アップロードによる並列性の向上、処理速度上昇、及びアップロードの中断/再開が可能になります。

Multipart UploadはもともとS3には機能としては備わっていますが、この機能を活用するためにはクライアントプログラム側で上述の特徴を活かすようなコードを記述しなければならず、活用するための敷居が少し高かったです。今回のリリースにより、これをSDKレベルでサポートしてくれたようです。素晴らしいですね。

というわけで、今日はMultipart Uploadを試してみました。

前提

AWS SDK PHPの動作にはPHP 5.5以上が必要です。私の手元の環境(Mac OS X Yosemite)はPHP 5.5.20がインストールされていたため特にアップデートは必要ありませんでしたが、自身のPHPのバージョンを確認しておいてください。

以前のバージョンと同様に、Composerを使ってインストールします。

~/tmp/$ php -v
PHP 5.5.20 (cli) (built: Feb 25 2015 23:30:53)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
~/tmp/$ mkdir php3 && cd php3

~/tmp/php3$ vim composer.json

~/tmp/php3$ cat composer.json
{
    "require": {
        "aws/aws-sdk-php": "3.*"
    }
}

~/tmp/php3$ curl -sS https://getcomposer.org/installer | php
#!/usr/bin/env php
All settings correct for using Composer
Downloading...

Composer successfully installed to: /Users/mochizukimasao/tmp/php3/composer.phar
Use it: php composer.phar
~/tmp/php3$ php composer.phar install
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing mtdowling/jmespath.php (2.2.0)
    Downloading: 100%

  - Installing guzzlehttp/promises (1.0.0)
    Downloading: 100%

  - Installing psr/http-message (1.0)
    Downloading: 100%

  - Installing guzzlehttp/psr7 (1.0.0)
    Downloading: 100%

  - Installing guzzlehttp/guzzle (6.0.1)
    Downloading: 100%

  - Installing aws/aws-sdk-php (3.0.3)
    Downloading: 100%

Writing lock file
Generating autoload files

~/tmp/php3$ ls
total 2200
drwxr-xr-x   7 mochizukimasao  staff      238  6  4 13:00 .
drwxr-xr-x  60 mochizukimasao  staff     2040  6  4 12:54 ..
-rw-r--r--   1 mochizukimasao  staff       60  6  4 12:54 composer.json
-rw-r--r--   1 mochizukimasao  staff    12010  6  4 12:57 composer.lock
-rwxr-xr-x   1 mochizukimasao  staff  1105630  6  4 12:55 composer.phar
drwxr-xr-x   9 mochizukimasao  staff      306  6  4 12:57 vendor

実装

上述のドキュメントに従って簡単なサンプルを書いてみました。

<?php

require 'vendor/autoload.php';


use Aws\S3\S3Client;
use Aws\S3\MultipartUploader;
use Aws\Exception\MultipartUploadException;

$source_file = '10mega.data';
$s3client = new S3Client([
    'version' => 'latest',
    'region'  => 'ap-northeast-1'
]);

$uploader = new MultipartUploader($s3client, $source_file, [
    'bucket' => 'mochizuki-example',
    'key'    => '10mega.data',
]);

try {
    $uploader->upload();
    echo "Upload complete.\n";
} catch (MultipartUploadException $e) {
    echo $e->getMessage() . "\n";
}

これだけでMultipart Uploadが実現できます!簡単ですね。ただし、アップロード中にN/Wの不調などが発生してしまってアップロードが中断されてしまった際には、500MB中の499MBまでアップロードが完了していたとしても全てロールバックされてしまいます。SDKではアップロードに失敗した時にMultipartUploadExceptionが投げられるので、そのExceptionに対して適切に処理を記述すればエラーが発生したとしても継続してアップロードを実行することが可能です。

エラー処理

上のコードのアップロード部分を、以下のとおり置き換えます。このコードもドキュメントに記載のものです。

do {
    try {
        echo 'upload start.'."\n";
        $result = $uploader->upload();
    } catch (MultipartUploadException $e) {
        echo $e->getMessage()."\n";
        sleep(5);
        $uploader = new MultipartUploader($s3client, $source_file, [
            'bucket' => 'mochizuki-example',
            'key'    => '10mega.data',
            'state' => $e->getState(),
        ]);
    }
} while (!isset($result));
echo 'upload complete.'."\n";

変更点としては、MultipartUploadExceptionをcatchした際に、再度ExceptionのgetStateを取得し、それをコンストラクタに渡してMultipartUploaderオブジェクトを再作成しています。そしてアップロードが完了するまでループで処理を継続するようにしています。これにより、途中でExceptionが発生したとしても処理を継続させることができるようになりました。

アップロードのエラーを再現するため、アップロード実行中にクライアント端末のWi-Fiを一時的にOffにします。想定では、N/Wが不調でExceptionが発生したとしてもリトライ処理が走るはずです。

upload start.
# ここでWiFiをOFF
An exception occurred while uploading parts to a multipart upload. The following parts had errors:
- Part 2: Error executing "UploadPart" on "https://s3-ap-northeast-1.amazonaws.com/mochizuki-example/10mega.data?partNumber=2&uploadId=5on.MntsoHsdMbRoByKdvvfqfbJAveLrBzbwxGSQVGU_EuCZDwhhhrmSq4bkaR.DsjDOds2rgXsAQGmVjwEYERDjQm_Fll4AaU8IgE.OuRoPtPMOkd5cqH3Kx4omZN7pZTwE2pikj.w75LalSryGnA--"; AWS HTTP error: cURL error 56: SSLRead() return error -36 (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)  (server): 100 Continue -
- Part 1: Error executing "UploadPart" on "https://s3-ap-northeast-1.amazonaws.com/mochizuki-example/10mega.data?partNumber=1&uploadId=5on.MntsoHsdMbRoByKdvvfqfbJAveLrBzbwxGSQVGU_EuCZDwhhhrmSq4bkaR.DsjDOds2rgXsAQGmVjwEYERDjQm_Fll4AaU8IgE.OuRoPtPMOkd5cqH3Kx4omZN7pZTwE2pikj.w75LalSryGnA--"; AWS HTTP error: cURL error 56: SSLRead() return error -36 (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)  (server): 100 Continue

<...エラーが続く...>
# WiFiをONにする

upload start.
upload complete.

正常にアップロードが再開されたのが確認できました。$e->getState()の結果をシリアライズして保存しておけば別プロセスからのアップロード再開などもできるようなので、これはまた試してみたいと思います。

まとめ

PHP SDK version 3の新機能「Multipart Uploadサポート」のご紹介でした。この他にも魅力的な機能がありますので、これから新規開発する時にはPHP SDK v3を利用してみてはいかがでしょうか!

おまけ

今の時期のシアトルの気候は最高です。

FullSizeRender

参考