LaravelでAWS S3へ画像をアップロードする

LaavelからAWS S3へ画像をアップロードする方法をまとめました。

# 前提条件

AWS S3でユーザ登録とバケットの作成ができていること。
※AWS S3の設定は[Rails, Laravel(画像アップロード)向けAWS(IAM:ユーザ, S3:バケット)の設定](https://qiita.com/nobu0717/items/4425c02157bc5e88d7b6)を参考にしてください

# S3パッケージのインストール

パッケージは[flysystem-aws-s3-v3](https://github.com/thephpleague/flysystem-aws-s3-v3)を使用します。
以下を実行してインストールしてください。

```
composer require league/flysystem-aws-s3-v3
```

# ファイルシステムの設定

`/config/filesystems.php`を以下のように編集します。

```php
  'default' => env('FILESYSTEM_DRIVER', 'local'),

  // 追記
  'cloud' => env('FILESYSTEM_CLOUD', 's3'),

  // 〜 略 〜

  'disks' => [
    'local' => [
      // 〜 略 〜
    ],
    'public' => [
      // 〜 略 〜
    ],

    // 以下を追記
    's3' => [
      'driver' => 's3',
      'key' => env('AWS_ACCESS_KEY_ID'),
      'secret' => env('AWS_SECRET_ACCESS_KEY'),
      'region' => env('AWS_DEFAULT_REGION'),
      'bucket' => env('AWS_BUCKET'),
    ],
```

# 環境変数の設定

プロジェクト直下にある`.env`ファイルを以下のように編集します。

```
AWS_ACCESS_KEY_ID='AWSで作成したAccess key ID'
AWS_SECRET_ACCESS_KEY='AWSで作成したSecret access key'
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET='作成したバケット名'
```

# Laravelの実装

今回は単純にcreateアクションでS3へアップロードします。
最低限のコードは次節以降になります。

## アップロード処理

### テーブルの作成

まずはデータベースにアップロード用のテーブルを作成するために、マイグレーションファイルを作成します。

```
php artisan make:migration create_posts_table
```

作成したマイグレーションファイルを以下のように編集します。

```php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            // 画像のパスを保存するカラムを追加
            $table->string('image_path')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
```

マイグレーションを実行します。

```
php artisan migrate
```

以上でテーブルの作成は完了です。

### Model

対応するModelを作成します。

```
php artisan make:model Post
```


### View

`/resources/views/post/create.blade.php`を作成します。
作成したファイルを以下のように編集します。

```html
@section('content')
  <form action="{{ action('PostsController@create') }}" method="post" enctype="multipart/form-data">
    <!-- アップロードフォームの作成 -->
    <input type="file" name="image">
    {{ csrf_field() }}
    <input type="submit" value="アップロード">
  </form>
@endsection
```

### Controller

Controllerを作成します。

```
php artisan make:controller PostsController
```

作成したControllerを以下のように編集します。

```php
<?php

// 略

use App\Post;
use Storage;

class PostsController extends Controller
{
  public function add()
  {
      return view('posts.create');
  }

  public function create(Request $request)
  {
      $post = new Post;
      $form = $request->all();

      //s3アップロード開始
      $image = $request->file('image');
      // バケットの`myprefix`フォルダへアップロード
      $path = Storage::disk('s3')->putFile('myprefix', $image, 'public');
      // アップロードした画像のフルパスを取得
      $post->image_path = Storage::disk('s3')->url($path);

      $post->save();

      return redirect('posts/create');
  }
}
```

以上で、画像のアップロードは完成です。

実際にアップロードして、バケットへ画像がアップロードできていることを確認してください。
例：

![3.png](https://qiita-image-store.s3.amazonaws.com/0/288694/d1030c4b-cc8d-8ebc-0f41-2d216b6bd3cb.png)


## 画像の表示

ローカル画像を表示する場合とほとんど同じです。

### View

`/resources/views/post/index.blade.php`を作成し、以下のように修正します。

```html
@section('content')
  @foreach($posts as $post)
    @if ($post->image_path)
      <!-- 画像を表示 -->
      <img src="{{ $post->image_path }}">
    @endif
  @endforeach
@endsection
```

### Controller

Controllerに以下のようにindexアクションを追加します。

```php
public function index(Request $request)
{
  $posts = Post::all();

  return view('posts.index', ['posts' => $posts]);
}
```

以上で、表示処理は完成です。

# 補足

うまく動作しない場合は、キャッシュをクリアしてみてください。

```
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
```

# 最後に

`flysystem-aws-s3-v3`パッケージを使用することで、手軽に画像のアップロードが可能でした。

# 参考
- [LaravelのFlysystem-aws-s3でAmazon S3と連携しファイル操作を行う](https://www.ritolab.com/entry/8)
- [LaravelでS3へファイルをアップロード・参照する](http://kkznch.hatenablog.com/entry/2018/03/06/154554)
- [Laravel キャッシュクリア系コマンドなど](https://qiita.com/Ping/items/10ada8d069e13d729701)