Laravelで作ったアプリをHerokuで動かそうと思ったら画像が表示されなかった話

背景

自分はLaravelで画像掲示板のようなものを作成していた。すると、ローカルでは画像が問題なく表示されていたのに、herokuにデプロイすると投稿された画像が「リンク切れ」状態になって表示されないという事象が起きた。そこで、その問題の原因と解決策についてまとめる。

原因

原因はシンプルにHerokuでは画像が生成されないということでした。 少し実装の話をさせてもらうと、今回の画像掲示板では画像をstorageに保存して、そのパスをDBに保存して管理していました。しかし、上記したようにherokuでは画像(ファイル)の生成ができないとのことです。(/tmpというフォルダにはファイルが生成できるようだが、それも一定時間で消えてしまうので根本的な解決にならない。)そのため、画像がリンク切れになってしまっていたようです。

解決策

「Herokuの仕様だから仕方ない、別にリリースするアプリじゃないし」と妥協してもよかったのですが、せっかくなのでどうにか足掻いてみることにしました。結果から言うと成功しました。どうやったかと言うと「画像自体をバイナリデータとしてDBに入れる」ことにしました。

画像をバイナリとしてDBに保存

下記にDBにバイナリを保存する方法を記述します。 詳細記事←ここに詳細は書いてあります。

手順

  1. 画像データ保存のためのカラムを用意する
  2. 画像のバイナリを取得する
  3. base64でエンコードする
  4. そのデータをDBに保存する
  5. viewで表示する こんな感じです。以下にその詳しい実装について記述します。

画像データ保存のためのカラムを用意する

Schema::create('bbs', function (Blueprint $table) {
    /*カラム*/
    $table->text('image'); // 画像に関する記述
});

バイナリ型もあったんですけど、MySQLの挙動とは違い、配列になったりで扱い不明だったのでtextにしました。

画像のバイナリを取得する

file_get_contents($request->image->getRealPath());

viewのフォームで「image」と言うname属性で画像を受け取るようにしたので、このような記述でバイナリデータが取れます。

base64でエンコードする

$image_binary = base64_encode(file_get_contents($request->image->getRealPath()));

ただのバイナリではHTMLで表示できないのでエンコードした状態で保存します。

そのデータをDBに保存する

Model::insert(["image" => $image_binary]);

そのバイナリをDBに保存。

viewで表示する

<img src="data:image/png;base64,<?= $image ?>">

表示するときは取ってきたbase64のデータをこんな感じで書くと表示されます。 以上。