PHP での S3 へのファイルのアップロード
最終更新日 2022年12月02日(金)
Table of Contents
ユーザーがアップロードしたファイルをアプリケーションで受信する必要がある場合、集中的で耐障害性のある場所にこれらのアップロードが保存されるようにする必要があります。
Heroku の一時的なファイルシステムでは、dyno を再起動すると、dyno のファイルシステムに書き込まれた情報はすべて失われます。 代わりに、Heroku ではバッキングサービスを推奨しています。 Amazon Simple Storage Service (S3) は、ファイルとメディアのための優れたストレージソリューションです。
この記事では、S3 を使用してファイルのアップロードを保存するように PHP アプリケーションを設定する方法を示します。
S3 の設定
この例では、Amazon Web Services (AWS) アカウントがセットアップ済みで、AWS アクセスキーペア (アクセスキー ID とシークレットアクセスキー) の用意ができていることを前提としています。セキュリティ資格情報の取得方法に関する AWS ドキュメントを参照してください。
アカウントを使用して新しい S3 バケットを作成するか、既存のバケットを再利用する必要もあります。
詳細については、Using AWS S3 to Store Static Assets and File Uploads](s3) ガイドを参照してください。このガイドには、詳細な背景知識、ステップ形式の S3 入門、役に立つヒントとコツが含まれています。
アプリケーションのセットアップ
Heroku アプリケーションと Git リポジトリをまだ作成していない場合、最初に作成します。
$ mkdir heroku-s3-example
$ cd heroku-s3-example
$ git init
$ heroku create
AWS SDK for PHP の使用
AWS SDK for PHP は、Composer を使用すると最も簡単にインストールできます。composer require
コマンドが最も簡単な方法です (aws/aws-sdk-php
パッケージを composer.json
に手動で追加することもできます)。
$ composer require aws/aws-sdk-php:~3.0
まだ追加していない場合、ここで vendor/
ディレクトリを .gitignore
に追加してください。バージョン管理が必要なのは composer.json
と composer.lock
だけで、vendor/
ディレクトリは不要です。
$ echo "vendor" >> .gitignore
ここで、すべてをコミットできます。
$ git add composer.* .gitignore
$ git commit -m "use aws/aws-sdk-php"
アプリケーションの環境設定
データベース接続情報、セキュリティ資格情報、またはその他のランタイム設定をハードコーディングすることは望ましくありません。代わりに、それらの情報をアプリケーションの環境に保存しておいて実行時に読み取ることをお勧めします。
Heroku アプリケーションの環境設定は、実行時に getenv()
または $_ENV
/$_SERVER
を使用して参照できます。このメカニズムを使用して、コードで AWS セキュリティキー (キー ID とシークレットキー) および S3 バケット名を動的に読み取ります。必要な手順は、Heroku CLI を使用して環境設定にこれら 3 つの情報 (以下のコードでは “aaa"、"bbb"、"ccc” のプレースホルダーを使用) を設定するだけです。
$ heroku config:set AWS_ACCESS_KEY_ID=aaa AWS_SECRET_ACCESS_KEY=bbb S3_BUCKET=ccc
不足しているのは、ファイルのアップロードを処理するコードだけです。
ファイルアップロードの処理
次に、アップロードするファイルをブラウザで受け付けて、クライアントのコンピュータ上の名前と同じ名前で S3 に保存する単純なスクリプトを作成します。
これは、適切な検証を行わず、クライアントでのファイル名をそのまま使用する、きわめて単純な例です。fileinfo を使用するなどして、ファイルの名前と内容を常に検証するようにしてください。また、カスタムのファイル名を生成するか、既存のファイルを上書きしないことを保証する別の方法を使用してください。ファイルの種類とサイズの制限を設けることもできます。
index.php
という名前のファイルに次の内容を含めます。
<?php
require('vendor/autoload.php');
// this will simply read AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from env vars
$s3 = new Aws\S3\S3Client([
'version' => '2006-03-01',
'region' => 'us-east-1',
]);
$bucket = getenv('S3_BUCKET')?: die('No "S3_BUCKET" config var in found in env!');
?>
<html>
<head><meta charset="UTF-8"></head>
<body>
<h1>S3 upload example</h1>
<?php
if($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['userfile']) && $_FILES['userfile']['error'] == UPLOAD_ERR_OK && is_uploaded_file($_FILES['userfile']['tmp_name'])) {
// FIXME: you should add more of your own validation here, e.g. using ext/fileinfo
try {
// FIXME: you should not use 'name' for the upload, since that's the original filename from the user's computer - generate a random filename that you then store in your database, or similar
$upload = $s3->upload($bucket, $_FILES['userfile']['name'], fopen($_FILES['userfile']['tmp_name'], 'rb'), 'public-read');
?>
<p>Upload <a href="<?=htmlspecialchars($upload->get('ObjectURL'))?>">successful</a> :)</p>
<?php } catch(Exception $e) { ?>
<p>Upload error :(</p>
<?php } } ?>
<h2>Upload a file</h2>
<form enctype="multipart/form-data" action="<?=$_SERVER['PHP_SELF']?>" method="POST">
<input name="userfile" type="file"><input type="submit" value="Upload">
</form>
</body>
</html>
この例では、アップロードされたファイルの ACL を設定して、パブリックに読み取り可能にします。異なる権限が必要な場合は、必要に応じて例を調整してください。バケットポリシーを使用して、アップロードされたファイルのバケットでのデフォルト権限を定義することもできます。
ここで、Heroku にこれを追加、コミット、およびプッシュすることができます。
$ git add index.php
$ git commit -m "file upload test form"
$ git push heroku master
デプロイが終了したら、heroku open
を実行するか、ブラウザでアプリケーションの URL を指定してアプリケーションをテストできます。画像やテキストファイルなどのファイルをコンピュータから選択してアップロードします。何も問題がなければ、アップロードされたファイルのリンクと共に成功メッセージが表示されます。