Fippiyのプログラム学習内容アウトプットBlog

日々の学習内容をアウトプットして振り返りを実施する。

Laravel開発、作成したアプリをデプロイして本番環境で動作させる【5】本番環境でS3を利用して画像を扱う

デプロイして本番環境でアプリを動作させるシリーズの続きです。

 

前回までで、ようやく本番環境でDBを扱えるようになりました。

しかし、画像ファイルアップロードが正常にできず、テキストのみDBに記録されている状態となっていました。

今回はこちらを解消することを目指します。

今回の目的

本番環境で新規登録時に画像を保存し表示できるようにする。

なぜやるか

DBにテキストデータを保存できるようにしたが、画像アップロードに失敗しているためこれを解消したい。

やりたいこと

本番環境で画像登録できるようにし、きちんと画像が表示されるようにする。

やったこと

Herokuでのファイルの扱いを確認

画像保存スペースの確保

画像保存領域のアクセス設定

アップロードを実装する為のコードを追加

変数内容の確認

本番環境の変数内容を見えるようにする

権限設定を疑う

 

実施内容

Herokuでファイルを扱う

画像アップロードの件について調べていたのですが、特にこちらを参考にさせて頂きました、ありがとうございます。

www.hypertextcandy.com

Git管理ではないデータであるアップロードされたファイルはHerokuのデプロイの度に消去されてしまうため、このままの運用は難しいようです。

AmazonのS3などに保存する形がいいようです。

S3についてはRails学習時に利用したことがあり、リソースもあるのでこちらを流用する形で今回はHerokuアプリ+S3で画像アップロードを実現することにしました。

画像保存にS3を使う

PHP+S3でアップロード処理を実施するにあたっては、以下のサイトを参考にさせて頂きました、ありがとうございます。

qiita.com

beyondjapan.com

作業の流れは以下の通り

  1. Laravelアプリ用IAMを作成してAWSにアクセスする
  2. composerを利用してAWS接続設定を追加
  3. S3画像アップロード処理を入れる

 S3へアクセスする為のユーザ設定

まずは、IAMの設定を上記サイトを見ながら実施しました。

GetObject,PutObjectを指定することで画像の保存と読み込みに対応させる内容となります。

次に、composerにAWS SDK for PHPをインストール

$ composer require aws/aws-sdk-php

 

アップロードの機能実装

画像アップロード処理を実施する上で、ローカル環境と本番環境で処理を分岐させたかったので、手順を確認しました。こちらを参考にさせて頂きました、ありがとうございます。

qiita.com

自分なりにコードの内容を理解して組み込めるのが一番いいのですが、それによって失敗もあるので、最低限自分が使用している変数を組み込んで、まずはコードをそのまま利用させて頂きました。

実装手順はこちら

  1. 全コードをまとめて適用せず、ある段階までを順番に適用し、エラーを解消しながら実装する。
  2. ローカル環境で一度S3アップロード対応できるか実装する。
  3. 本番環境とテスト環境を分ける記述をつけて本番環境のみS3へアップロードできるようにする。

 

変数内容を確認してデータが反映されているのを確認する

まず、ある程度の変数の読み替えと途中段階までのコードを適用させました。

そして、ブレークポイントを設定して、データの内容を確認してみることにしました。

f:id:Fippiy:20190325150940p:plain

S3clientまでを試す

こちらの設定はS3クライアント設定を入れる物で、環境変数の値を読み込ませています。この当たりは問題なく動作してくれました。

 

この先のコードについて確認していたところ、そもそも理解できていない所もあったので、PHPの基本にたちかえり$_FILESという記述をしっかり確認することにしました。 

この内容についてはこちらを参考にさせて頂きました、ありがとうございます。

wepicks.net

改めてコードを最初から見直しを実施しました。

今回のコード内容から考えると、formから送信されたPOSTデータが取得されていることを確認しました。

確認には、デバッグツールのブレークポイントを設定し、プログラムが一時停止したところで対象変数を入力し確認します。

f:id:Fippiy:20190325152939p:plain

デバッグ時の変数内容表示

"$_FILES"を指定すると、今回のformから送信されるpictureに関係するデータが出力されました。

"$_FILES['profile']['name']"と入力することで"7474…_m.jpg"のみが返り値として取得できます。

さらに画像アップロードについて確認すると"$_FILES['name']['tmp_name']"の項目にアップロードされたファイルの情報がはいることが分かりました。

アップロードについてはこちらを参考にさせて頂きました、ありがとうございます。

www.flatflag.nir87.com

ここで改めて、先程の$_FILESを確認してみると…。

f:id:Fippiy:20190325152939p:plain

デバッグ時の変数内容表示

tmp_nameは何もなし、更によく見るとerrorが1となっています。

どうやらアップロード画像に何か問題あるのか?といろいろ調べてみました。

この問題についてはこちらを参考にさせて頂きました、ありがとうございます。

tamulapin.hatenablog.com

画像ファイルが大きすぎるとエラーになるようです。

エラーになる画像ファイルのサイズを変更するか、そもそも受付しないようにするか…いずれにしても画像ファイル容量が小さい物で再度試しました。

f:id:Fippiy:20190325161452p:plain

デバックデータ表示(小サイズ画像)

情報が表示されました。

※sizeって画像の縦横の大きさのことかと思っていましたが、データーサイズでした。

 

ここまで確認できましたので、段階的に実装していたコードの続きを実装していきます。 

次のコードはこちら。

f:id:Fippiy:20190325165552p:plain

画像アップロード・保存先URL

画像をアップロードするコードになります、画像アップロード結果が$rewultに入るので、こちらの結果からアップロード先であるS3に保存した画像のパスを取得しています。

このパスをDBのpictureに保存しておけば、一覧表示の時にpictureカラムを指定してあげるとS3にアップロードされた画像を参照することができます。

これで、ローカル環境ですが画像アップロード先をS3にしてアップロードすることができました。

 

 本番環境でS3対応させる

今回最終的に実施したい内容としては、開発環境の画像保存は従来通りローカルで保存を実施、本番環境ではS3に画像を保存させることを目的としています。

ですので、ここで開発環境と本番環境の設定分岐をいれました。

本番環境がURLで画像を参照する形としたので、開発環境も同様にURL参照する形に変更して、ビュー側の表現を統一することにしました。

f:id:Fippiy:20190328160524p:plain

開発と本番環境を分岐させてコード記述

初めに開発環境を指定してストレージ領域へ画像保存を実施、本番環境時はS3に保存の処理を書いています。

 

後は実際に本番環境にコード内容を反映させて動作確認する必要があります。

変更内容をマージしてHerokuにデータ反映を実施しました。

画像アップロード処理を早速実施するもエラー。

ちょっと悩むも、本番環境用の環境変数AWS関連情報を設定するのを忘れていました。早く気がついてよかった…。

 

環境変数を設定し、再度アップロードを試すも再びエラーとなりました。

本番環境エラー表示出力

f:id:Fippiy:20190325173747p:plain

本番環境エラー

 No such file or directoryとでました、ファイルかディレクトリがないようです。

 

デバッグ環境を準備していたので、デバッグ設定を追加して開発環境と本番環境での結果を比較することにしました。

f:id:Fippiy:20190326120936p:plain

デバッグ設定追加

\Debugbar::info(.....);とデバッグ設定しておくことで、デバッグツールでPOSTデータ送信時にこの変数の値を確認します。

 

ローカルでの確認結果です。上から$FILES['picture']、$tmpname、$new_filenameの3項目を確認しました。

f:id:Fippiy:20190326121121p:plain

デバッグ(ローカル)

$tmpnameの/tmp/はないため、特に値として変化はなし。

$new_filenameについて"bookimages//〜"と"/"が2つあるもはおかしいのでコレが原因かな?(が、ローカルはきちんと画像処理は完了している)

続いて、本番環境での確認結果です。同様に上から$FILES['picture']、$tmpname、$new_filenameの3項目を確認。

f:id:Fippiy:20190326121847p:plain

デバッグ(本番環境)

$FILES['picture']の中にあるtmp_nameと$tmpnameのパスが変わっています。

$tmpnameのコードを確認すると

# ~/app/Http/Controllers/BookController.php

$tmpname = str_replace('/tmp/','',$_FILES['picture']['tmp_name']);

 

replaceで'/tmp/'を''へ置き換えるという記述です。

置換えをしているのでパスは変わりますよ…。

元々作成されていた方が必要として設定していた内容を使わせて頂いていたのですが、今回わざわざパス変更することはないので、見直す事にしました。パスが変わらなければ同じデータを扱えると考え、この記述はなくしました。

 

コード修正後にあらためてデバッグ結果を確認しました。 

開発環境

bookimages//private/var/folders/x2/96c_zgys1yvfp4905c063n_80000gn/T/phprie2aF.jpg

本番環境

bookimages//tmp/phpd4p9De.jpg

 

"//"と2つあるのはおかしな感じだったので、こちらも修正することにしました。

"/"が余分に表示されていたので、削除しています。

before

  $new_filename = 'bookimages/'.$tmpname.'.'.$ext;

after

  $new_filename = 'bookimages'.$tmpname.'.'.$ext;

 

ここまで修正を実施すればようやく解決かと思いきや…。

 

エラーが変化しました。

Error executing "PutObject" on "https://ユーザ名.s3.ap-northeast-1.amazonaws.com/bookimages/phpGBZRiv.jpg"; AWS HTTP error: Client error: `PUT https://ユーザ名.s3.ap-northeast-1.amazonaws.com/bookimages/phpGBZRiv.jpg` resulted in a `403 Forbidden` response:

 

エラーの内容をよく確認すると、どうやら権限がないようなことを言っているようです。

今回、このアプリ用にS3にアクセスする為のIAMユーザを新規作成してました。

これが問題の可能性があると考え、動作が確認できている既存アプリのユーザで通信できるかを一度確認してみることにしました。

本番の環境変数設定のAWS関連情報を書き換えて、再度アップロードを実施。

…アップロードできました…。

どうやら、残ってた問題はユーザの権限まわりだった模様。

本番環境の設定にこんなに手こずるとは思いませんでした…。

 

これでようやく本番環境S3で画像アップロードが完了です。

 

しかし、これで新規作成時のアップロードが実装できただけです。

後はレコードの編集と削除時の対応がまだありますので、次はこれに取り組んでみたいと思います。

 

fippiy.hatenablog.jp