Amazon S3を画像サーバとして使う&PHPから画像アップロード

お仕事でS3を画像置き場にすることがあったので、その手順をまとめます:star2:
そんな記事他にいくらでもあるって？:person_with_pouting_face:・・・:see_no_evil::hear_no_evil:

# AWSコンソールでの作業
アカウントは既にあるものとします。

## S3バケットを作成
ログイン後S3のページに行きます。そこで"+バケットを作成する"を押します。
![0.png](https://qiita-image-store.s3.amazonaws.com/0/84917/7eec0663-6e5e-4c65-3a64-48bd6cba7d5b.png)

バケット名、リージョン、既存のバケットからコピーする場合はコピー元を入力します。
![1.png](https://qiita-image-store.s3.amazonaws.com/0/84917/b6ee3761-8de4-3298-9f60-4310a4d0f153.png)
バケット名についてはDNS準拠となっていますが、英数字とハイフンを使って常識的な名前にすれば、ほぼほぼ問題ないです。問題があればすぐにエラーが出てくれますし。
リージョンは日本で使うなら東京を選ぶといいと思います。

次にプロパティの設定をします。バージョニングやらロギングやら、必要なものがあれば有効にしてください。
![2.png](https://qiita-image-store.s3.amazonaws.com/0/84917/9bb0e14a-3791-d669-3d05-d70b2bebbbca.png)

次に権限周りの設定をします。
![3.png](https://qiita-image-store.s3.amazonaws.com/0/84917/9fac9314-3cfe-83fb-eeab-0299f1f7217b.png)
ユーザについてはとりあえず管理者に読み書きを与えておきましょう。PHPからアクセスする用の権限を絞ったユーザは後ほど作成します。
パブリックアクセスは許可します。今回は画像サーバとして各クライアントが直でアクセスできるようにしたいからです。仰々しいアラートが出ますが、大丈夫です。ただし、人に見られて困るようなファイルは絶対に置かないでください。

最後に入力に誤りが無いか確認し、問題なければバケットを作成します。

## アクセス用ユーザを作成
PHPからS3にアクセスする用に、ファイルのアップデートのみ権限を持つユーザを作成します。

IAMのユーザのページに行きます。そこで"ユーザを追加"を押します。
![4.png](https://qiita-image-store.s3.amazonaws.com/0/84917/ec388f09-9de9-1547-b289-cdf0cb5421ca.png)

名前は適当なもので良いです。
アクセスの種類でプログラムによるアクセスにチェックをつけます。
![5.png](https://qiita-image-store.s3.amazonaws.com/0/84917/2b6bdd7b-3b5c-2b7b-d5df-f16d2dda35ef.png)
後は特に何も設定せず、ユーザを作成してしまいます。アクセス権が何もないという警告が出ますが、気にしないで大丈夫です。

**ユーザ作成時にアクセス情報が載ったCSVファイルをダウンロードすることが出来ます。
この中の情報を後でPHPで利用するので、必ずダウンロードしておいてください。**

次に作成したユーザを選択し、詳細画面に行きます。そこで"インラインポリシーの追加"を押します。
画像はポリシーを追加した後に撮ったので既にアタッチ済みのものがありますが、最初は何も無いはずです。
![6.png](https://qiita-image-store.s3.amazonaws.com/0/84917/cb789b30-fb43-999b-869d-0e093c5eeb11.png)

新しいポリシーを作ります。内容は、S3の全てのリソース(パス)にファイルのアップロードができる、というものです。
![7.png](https://qiita-image-store.s3.amazonaws.com/0/84917/0cef1a71-b4ee-5da9-d741-bcd765cb4533.png)
サービスはS3を選択します。
アクションは`PutObject`と`PutObjectAcl`を選択します。`PutObjectAcl`のほうは、アップロードしたファイルをパブリックにするために必要になります。
リソースは全てのリソースにします。今回は一つのバケットをまるまる画像置き場にするので特に気にする必要が無いですが、バケット内でディレクトリを切ってどこかを画像置き場にする場合は、そのパスを指定します。

次に適当な名前をつけてポリシーを作成します。
![8.png](https://qiita-image-store.s3.amazonaws.com/0/84917/b0e8be2f-3079-b88b-77f7-b46ae1aa8962.png)

これでAWSコンソールの作業は終了です。

# PHP関連の作業

## ライブラリのインストール
まずはAWS SDK for PHPをcomposerを使ってインストールします。
composer.jsonがまだ無い場合は上、ある場合は下でインストール出来ます。

```
composer install aws/aws-sdk-php
```

```
composer require aws/aws-sdk-php
```

## アップロード処理を書く
まずは`S3Client`を生成します。先程ユーザ作成時にダウンロードしたCSVにかかれているアクセス情報を使用します。リージョンも東京以外を使う場合は書き換えてください。

```php
$s3client = new Aws\S3\S3Client([
    'credentials' => [
        'key' => '*Access key ID*', // ここは各自書き換える
        'secret' => '*Secret access key*', // ここは各自書き換える
    ],
    'region' => 'ap-northeast-1', // 東京の場合
    'version' => 'latest',
]);
```

その`S3Client`で`putObject`を実行します。失敗すると例外が吐かれるので、`try-catch`で囲んでやります。

```php
try {
    $result = $s3client->putObject([
        'ACL' => 'public-read', // アップロードしたファイルをパブリックにするために必要
        'Bucket' => '*backet name*', // バケット名
        'Key' => '*file name*', // バケット内のファイル名
        'SourceFile' => $_FILES['image']['tmp_name'], // 元のファイル名
        'ContentType' => mime_content_type($_FILES['image']['tmp_name']),
    ]);
    $url = $result['ObjectURL']; // アップロードしたファイルのURLが取得できる
    // do success action
} catch (S3Exception $e) {
    // do failure action
}
```

これでS3に画像をアップロードすることが出来ます。上のプログラム中の`$url`の内容をブラウザで叩いてみれば、アップロードした画像が表示されます。

### おまけ：`image`というキーでAPIサーバに送られてきた画像をS3にアップロードする関数

```php:関数全体
private function uploadImage()
{
    if (isset($_FILES['image'])) {
        $s3client = new Aws\S3\S3Client([
            'credentials' => [
                'key' => '*Access key ID*',
                'secret' => '*Secret access key*',
            ],
            'region' => 'ap-northeast-1',
            'version' => 'latest',
        ]);
        try {
            $result = $s3client->putObject([
                'ACL' => 'public-read',
                'Bucket' => '*backet name*',
                'Key' => '*file name*',
                'SourceFile' => $_FILES['image']['tmp_name'],
                'ContentType' => mime_content_type($_FILES['image']['tmp_name']),
            ]);
            return [
                'result' => 'ok',
                'url' => $result['ObjectURL'],
            ];
        } catch (S3Exception $e) {
            return [
                'result' => 'ng',
                'errors' => $e->getMessage(),
            ];
        }
    }
    return [
        'result' => 'ok',
        'url' => '',
    ];
}
```

# おわりに
AWSはサクッと使えるようになってとてもいいですね:ok_hand:セキュリティやパフォーマンスを考えるとこれではダメなのかも知れませんけど:skull:
ほぼ落ちることが無いし、メンテする必要も無いし、良いサービスだと思います:high_brightness:
S3以外にはEC2とRDSは使ったことがありますが、他にも便利なものがありそうなのでどんどん試して行きたいです:punch: