なぜこれを作ったのか?
- 30MB以上のファイルを数多くアップロードする案件があって、サーバ経由でS3にファイルを送るとサーバの負荷がかかる。それを減らすためにブラウザとS3の間で直接通信をする方法を選んだ。
- ユーザがアップロードするファイルサイズは 10MB ~ 100MB くらいの幅があると考えられる。
- 今後の案件で 500MB くらいのファイルサイズもあるかも。
S3 ファイルサイズ要件
- 1 回の PUT オペレーションでアップロードできる最大サイズは 5 GB です。
- 100 MB 以上の場合は、マルチパートアップロードを考慮してください。
- 完了リクエストが正常に送信されなかった場合、パートはS3に残るためパートに対して支払いが発生します。
調べて分かったこと
- javascript用のSDKを使う方法と、もう一つは、php用のSDKを使ってURLを作成してからjavascriptで送信する方法がある。
- javascript用のSDKを使う場合、evaporate.js というライブラリがあるようです。
- php用のSDKを使う場合、phpでURLを発行してjavascriptでS3へマルチパートアップロードをする。この場合は、完了処理のときにPHPで一括してETagを取得する。
- マルチパートアップロードでアップロード途中のファイルはS3の管理画面上に表示されない。そのため、アップロードに失敗し(中止処理もおこなわれなかっ)たファイルはライフサイクルで削除する設定が必要。※削除する設定をしないで放っておくと見えないまま課金されますので注意してください。
案件で node.js を使っていないので、PHP(署名) + ブラウザのjavascriptで送信という処理を選択しました。
動作確認環境
- サーバ: AWS ではない CentOS7 環境
- PHP 7.2
- aws-sdk-php: 3.52
- signature_version: v4
Github
こちらにすべてのソースコードを置いてます。
https://github.com/gold1/aws_s3_upload_sample
インストール
$ git clone https://github.com/gold1/aws_s3_upload_sample.git
$ cd aws_s3_upload_sample
$ mkdir download
$ chmod 777 download
$ composer install
config.php にAWSのIAMのキーとバケット名を指定してください。
$config = array(
'key' => '',
'secret' => '',
'bucket' => '',
);
S3 のバケットのCORSの設定を変更して、ブラウザからアップロードできるようにしてください。
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
S3のバケットのライフサイクルの設定で失敗したマルチパートアップロードを一定期間後に削除するようにしてください。
不完全なマルチパートアップロードをクリーンアップする
次の後: 3 Days アップロード開始からの日数
Apache でアクセス
test1.html : ブラウザからアップロードするサンプル
test2.html : ブラウザからマルチパートアップロードするサンプル
server3.php : PHPでアップロードするサンプル
ソースファイルの補足
- マルチパートアップロード後に listParts() という関数で ETag を取得しています。
- このサンプルは、マルチパートアップロードのファイルサイズが200MBくらいでエラーになるバグが残っています(未解決)。test1.html の通常のアップロードのサンプルは800MBで問題なく動作しました。
参考