AUTOVICE

TECH BLOG

開発環境と本番環境で画像のアップロード先を分岐する方法

2020-03-04

はじめに

この記事では、以下の方針で画像のアップロード先を分岐する方法について解説しています。

  • 開発環境:ローカル(storage/app/public/)
  • 本番環境:Amazon S3

事前準備

AWSに未登録の場合は以下を参考にして登録してください。
AWS アカウント作成の流れ | AWS

IAM/S3が未作成の場合は以下を参考にして作成してください。
また、本番環境のアップロード方法で必要になるので、Laravelの実装も行ってください。
超簡単!LaravelでS3を利用する手順 - Qiita

Amazon S3へのアップロードができない場合は、以下の記事を参照してください。
【Laravel】Amazon S3へのアップロードが「403 Access Denied」で失敗する原因と対処方法 - AUTOVICE

アップロード先の分岐方法

実装

アップロードの実装を以下のように変更します。

    public function store(Request $request)
    {
        $post = new Post();

        # 画像ファイルのアップロード
        $image = $request->file('image');
        if ( app()->isLocal() || app()->runningUnitTests() ) {
            # 開発環境
            $path = $image->store('public/images');
            $post->image = Storage::url($path);
        }
        else {
            # 本番環境
            $path = Storage::disk('s3')->put('/', $image, 'public');
            $post->image = Storage::disk('s3')->url($path);
        }

        Auth::user()->posts()->save($post);

        return redirect()->route('posts.index');
    }

解説

開発環境もしくはテスト環境の場合は画像をローカルに保存し、それ以外、つまり本番環境の場合はS3に保存しています。

app()->isLocal()メソッドなどは以下の設定ファイルを見て判断しています。config/app.phpの42行目あたりに以下の設定があります。

    'env' => env('APP_ENV', 'production'),

.envAPP_ENVを参照しています。.envは以下のようになっています。

APP_ENV=local

localに設定されています。つまり、.envファイルがあるローカル環境では開発環境であるということになります。

では、本番環境ではどうなるでしょうか。

例としてHerokuにデプロイしたとします。Heroku(に限りませんが)には機密情報を含んだ.envはデプロイされません。そのため、Herokuに環境変数を設定するときはheroku config:set XXXXX=XXXXXといったコマンドを手動で実行する必要があります。つまり、.envがなく、環境変数を手動で登録していない限り、APP_ENVはないことになります。

もう一度config/app.phpを見てみます。

    'env' => env('APP_ENV', 'production'),

APP_ENVが参照できない場合、第2引数のproductionが適用され、本番環境になるというわけです。

一応、本当にそうなっているか確認してみます。ターミナルで以下のコマンドを実行します。

$ heroku run php artisan tinker
>>> config('app.env');
=> "production"

heroku runコマンドは、この跡に続くコマンドをHeroku上で実行するコマンドです。つまり、HerokuにデプロイしたLaravelプロジェクトにアクセスしています。

想定通り、本番環境になっていました。もちろん、HerokuにAPP_ENVは設定していません。

アップロードしたファイルを削除する方法

開発環境の場合はローカルにあるファイルを削除し、本番環境の場合はS3にあるファイルを削除します。

    public function destroy(Post $post)
    {
        Post::destroy($post->id);

        if ( app()->isLocal() || app()->runningUnitTests() ) {
            Storage::delete('public/images/' . basename($post->image));
        }
        else {
            Storage::disk('s3')->delete(basename($post->image));
        }

        return redirect()->route('posts.index');
    }

アップロードの処理と同様、条件分岐で開発環境と本番環境で処理を分けています。deleteメソッドはディレクトリ名まで含めて指定する必要があるのでご注意ください。

まとめ

Ruby on RailsのActive Storageは、ロジックで分岐しなくても環境によってアップロード先を判断してくれましたが、Laravelはこのようにロジックとして実装する必要があります。

Ruby on Railsは実装する上で楽ではありましたが、ロジックがブラックボックスに隠れてしまっていて、初めのうちは把握しづらいというデメリットがあります。

個人的にはLaravelのほうがわかりやすいかなと思います。