LaravelでS3へファイルをアップロード・参照する

※本記事は嘉数の個人ブログ(LaravelでS3へファイルをアップロード・参照する - けけずんセルフハッキング)からの転載になります

概要

LaravelでアップロードされたファイルをS3に保存・参照する。

ファイルアップロード時の処理は下図の通り、クライアントからLaravelを通ってS3に保存される。

f:id:kkznch:20180620092248p:plain

ファイルを参照する際は下図の通り、Laravelが対象となるファイルのURLをS3から取得してページにリンクする。URLからS3上のファイルを参照するために、対象となるファイルはPublicに公開される必要がある。

f:id:kkznch:20180620092251p:plain

環境

  • Laravel 5.5.36

Laravelが動作するEC2、ファイル保存・参照先となるS3バケットは作成済みとする。

手順

AWS側の操作

AWSコンソールへログインし、以下の操作を行う。

IAMユーザーの作成

サービス「IAM」からサイドバーの「ユーザー」をクリックし、続いて以下の操作を行う。

  1. 「ユーザーを追加」をクリックする
  2. 「ユーザー名」を入力する
  3. 「プログラムによるアクセス」にチェックを入れる
  4. 「次のステップ:アクセス権限」をクリックする
  5. 「ユーザーをグループに追加」を選択する
  6. 「次のステップ:確認」をクリックする
  7. 「ユーザーの作成」をクリックする

ここでユーザの作成が完了する。このとき、作成したユーザーのセキュリティ認証情報が記述されたファイル(CSV)がダウンロード出来るので、ダウンロードしておく。

IAMユーザのアクセス権限を設定

サービス「IAM」からサイドバーの「ユーザー」をクリックし、続いて以下の操作を行う。

  1. 作成したユーザーをクリックする
  2. 「アクセス権限」タブを選択し、「インラインポリシー」をクリックする
  3. 「ビジュアルエディタ」タブにある項目を以下のように入力する
    1. 「サービスの選択」:S3
    2. 「アクション」:
      • 「GetObject」にチェック
      • 「PutObject」にチェック
      • 「DeleteObject」にチェック
      • 「PutObjectAcl」にチェック
    3. 「リソース」:
  4. 「Review policy」をクリックする
  5. 「名前」を入力する
  6. 「Create a policy」をクリックする

ちなみにJSONは以下のようになる。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::mybucket/myprefix/*"
        }
    ]
}

Resource要素の末尾に*がないと指定したプレフィックス(今回はmyprefix)以下を操作する権限がないと言われるので注意!

Laravel側の操作

パッケージをインストール

Laravelプロジェクトフォルダ下で以下のコマンドを入力する。

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

これによりLaravelのファイルシステムでファイルの保存・参照先をS3に向けるためのパッケージがインストールされる。

.envを編集

.envファイルに以下を追記する。

AWS_S3_KEY=[AWS S3接続用ユーザのAccess Key ID]
AWS_S3_SECRET=[AWS S3接続用ユーザのSecret Key]
AWS_S3_REGION=[AWS S3設置リージョン]
AWS_S3_BUCKET=[AWS S3のバケット名]

config/filesystems.phpを編集

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

<?php
return [
    'default' => 'local',
    'cloud' => 's3',

    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
        ],

        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],

+        's3' => [
+            'driver' => 's3',
+            'key' => env('AWS_S3_KEY'),
+            'secret' => env('AWS_S3_SECRET'),
+            'region' => env('AWS_S3_REGION'),
+            'bucket' => env('AWS_S3_BUCKET'),
+        ],

    ],

];

Controllerの作成

以下のコマンドでControllerを作成する。

$ php artisan make:controller UploadContentController

app/Http/Controllers/UploadContentController.phpを以下のように編集する。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class UploadContentController extends Controller
{
    public function index()
    {
        return view('upload');
    }

    public function store(Request $request)
    {
        $this->validate($request, ['myfile' => 'required|image']);

        $image = $request->file('myfile');

        /**
         * 自動生成されたファイル名が付与されてS3に保存される。
         * 第三引数に'public'を付与しないと外部からアクセスできないので注意。
         */
        $path = Storage::disk('s3')->putFile('myprefix', $image, 'public');

        /* 上記と同じ */
        // $path = $image->store('myprefix', 's3');

        /* 名前を付与してS3に保存する */
        // $filename = 'hoge.jpg';
        // $path = Storage::disk('s3')->putFileAs('myprefix', $image, $filename, 'public');

        /* ファイルパスから参照するURLを生成する */
        $url = Storage::disk('s3')->url($path);

        return redirect()->back()->with('s3url', $url);
    }
}

Viewの作成

resources/views/upload.blade.phpを作成し、以下のようにする。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Upload S3 Test</title>
</head>

<h1>S3アップロードテスト</h1>

{!! Form::open(['url' => '/upload', 'method' => 'post', 'class' => 'form', 'files' => true]) !!}

<div class="form-group">
{!! Form::label('myfile', 'Upload a file') !!}
{!! Form::file('myfile', null) !!}
</div>

<div class="form-group">
{!! Form::submit('Upload') !!}
</div>

{!! Form::close() !!}

@if (session('s3url'))
    <h1>いまアップロードしたファイル</h1>
    <img src="{{ session('s3url') }}">
@endif
</html>

考察

LaravelからS3に対してファイルのアップロード及びファイルの参照を行うことができた。Controller内でファイルアップロード時のS3のプレフィックスを指定している箇所があるが、毎回同じプレフィックスを指定するの面倒くさいな。何かいい方法ないかな。

参考