Laravel開発、作成したアプリをデプロイして本番環境で動作させる【5】本番環境でS3を利用して画像を扱う
デプロイして本番環境でアプリを動作させるシリーズの続きです。
前回までで、ようやく本番環境でDBを扱えるようになりました。
しかし、画像ファイルアップロードが正常にできず、テキストのみDBに記録されている状態となっていました。
今回はこちらを解消することを目指します。
今回の目的
本番環境で新規登録時に画像を保存し表示できるようにする。
なぜやるか
DBにテキストデータを保存できるようにしたが、画像アップロードに失敗しているためこれを解消したい。
やりたいこと
本番環境で画像登録できるようにし、きちんと画像が表示されるようにする。
やったこと
Herokuでのファイルの扱いを確認
画像保存スペースの確保
画像保存領域のアクセス設定
アップロードを実装する為のコードを追加
変数内容の確認
本番環境の変数内容を見えるようにする
権限設定を疑う
実施内容
Herokuでファイルを扱う
画像アップロードの件について調べていたのですが、特にこちらを参考にさせて頂きました、ありがとうございます。
Git管理ではないデータであるアップロードされたファイルはHerokuのデプロイの度に消去されてしまうため、このままの運用は難しいようです。
AmazonのS3などに保存する形がいいようです。
S3についてはRails学習時に利用したことがあり、リソースもあるのでこちらを流用する形で今回はHerokuアプリ+S3で画像アップロードを実現することにしました。
画像保存にS3を使う
PHP+S3でアップロード処理を実施するにあたっては、以下のサイトを参考にさせて頂きました、ありがとうございます。
作業の流れは以下の通り
S3へアクセスする為のユーザ設定
まずは、IAMの設定を上記サイトを見ながら実施しました。
GetObject,PutObjectを指定することで画像の保存と読み込みに対応させる内容となります。
次に、composerにAWS SDK for PHPをインストール
$ composer require aws/aws-sdk-php
アップロードの機能実装
画像アップロード処理を実施する上で、ローカル環境と本番環境で処理を分岐させたかったので、手順を確認しました。こちらを参考にさせて頂きました、ありがとうございます。
自分なりにコードの内容を理解して組み込めるのが一番いいのですが、それによって失敗もあるので、最低限自分が使用している変数を組み込んで、まずはコードをそのまま利用させて頂きました。
実装手順はこちら
- 全コードをまとめて適用せず、ある段階までを順番に適用し、エラーを解消しながら実装する。
- ローカル環境で一度S3アップロード対応できるか実装する。
- 本番環境とテスト環境を分ける記述をつけて本番環境のみS3へアップロードできるようにする。
変数内容を確認してデータが反映されているのを確認する
まず、ある程度の変数の読み替えと途中段階までのコードを適用させました。
そして、ブレークポイントを設定して、データの内容を確認してみることにしました。
こちらの設定はS3クライアント設定を入れる物で、環境変数の値を読み込ませています。この当たりは問題なく動作してくれました。
この先のコードについて確認していたところ、そもそも理解できていない所もあったので、PHPの基本にたちかえり$_FILESという記述をしっかり確認することにしました。
この内容についてはこちらを参考にさせて頂きました、ありがとうございます。
改めてコードを最初から見直しを実施しました。
今回のコード内容から考えると、formから送信されたPOSTデータが取得されていることを確認しました。
確認には、デバッグツールのブレークポイントを設定し、プログラムが一時停止したところで対象変数を入力し確認します。
"$_FILES"を指定すると、今回のformから送信されるpictureに関係するデータが出力されました。
"$_FILES['profile']['name']"と入力することで"7474…_m.jpg"のみが返り値として取得できます。
さらに画像アップロードについて確認すると"$_FILES['name']['tmp_name']"の項目にアップロードされたファイルの情報がはいることが分かりました。
アップロードについてはこちらを参考にさせて頂きました、ありがとうございます。
ここで改めて、先程の$_FILESを確認してみると…。
tmp_nameは何もなし、更によく見るとerrorが1となっています。
どうやらアップロード画像に何か問題あるのか?といろいろ調べてみました。
この問題についてはこちらを参考にさせて頂きました、ありがとうございます。
画像ファイルが大きすぎるとエラーになるようです。
エラーになる画像ファイルのサイズを変更するか、そもそも受付しないようにするか…いずれにしても画像ファイル容量が小さい物で再度試しました。
情報が表示されました。
※sizeって画像の縦横の大きさのことかと思っていましたが、データーサイズでした。
ここまで確認できましたので、段階的に実装していたコードの続きを実装していきます。
次のコードはこちら。
画像をアップロードするコードになります、画像アップロード結果が$rewultに入るので、こちらの結果からアップロード先であるS3に保存した画像のパスを取得しています。
このパスをDBのpictureに保存しておけば、一覧表示の時にpictureカラムを指定してあげるとS3にアップロードされた画像を参照することができます。
これで、ローカル環境ですが画像アップロード先をS3にしてアップロードすることができました。
本番環境でS3対応させる
今回最終的に実施したい内容としては、開発環境の画像保存は従来通りローカルで保存を実施、本番環境ではS3に画像を保存させることを目的としています。
ですので、ここで開発環境と本番環境の設定分岐をいれました。
本番環境がURLで画像を参照する形としたので、開発環境も同様にURL参照する形に変更して、ビュー側の表現を統一することにしました。
初めに開発環境を指定してストレージ領域へ画像保存を実施、本番環境時はS3に保存の処理を書いています。
後は実際に本番環境にコード内容を反映させて動作確認する必要があります。
変更内容をマージしてHerokuにデータ反映を実施しました。
画像アップロード処理を早速実施するもエラー。
ちょっと悩むも、本番環境用の環境変数にAWS関連情報を設定するのを忘れていました。早く気がついてよかった…。
環境変数を設定し、再度アップロードを試すも再びエラーとなりました。
本番環境エラー表示出力
No such file or directoryとでました、ファイルかディレクトリがないようです。
デバッグ環境を準備していたので、デバッグ設定を追加して開発環境と本番環境での結果を比較することにしました。
\Debugbar::info(.....);とデバッグ設定しておくことで、デバッグツールでPOSTデータ送信時にこの変数の値を確認します。
ローカルでの確認結果です。上から$FILES['picture']、$tmpname、$new_filenameの3項目を確認しました。
$tmpnameの/tmp/はないため、特に値として変化はなし。
$new_filenameについて"bookimages//〜"と"/"が2つあるもはおかしいのでコレが原因かな?(が、ローカルはきちんと画像処理は完了している)
続いて、本番環境での確認結果です。同様に上から$FILES['picture']、$tmpname、$new_filenameの3項目を確認。
$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で画像アップロードが完了です。
しかし、これで新規作成時のアップロードが実装できただけです。
後はレコードの編集と削除時の対応がまだありますので、次はこれに取り組んでみたいと思います。