W

php artisan storage:link とは?画像が表示されないときに

#Laravel #ファイルアップロード #artisan #トラブルシューティング

画像をアップロードする機能を作った。保存はできているはずなのに、画面に表示すると画像が出ない。リンクを開くと「404 Not Found(見つかりません)」になる。

Laravel で画像を扱うとき、多くの人が最初につまずくところです。原因はたいてい1つで、php artisan storage:link を実行していないことです。

この記事では、このコマンドが何をしているのかを仕組みから説明します。

結論(急いでいる人向け)

ターミナル(黒い画面)でプロジェクトのフォルダに移動し、次を実行します。

php artisan storage:link

これで表示されるようになります。以下で「なぜこれが必要なのか」を説明します。

なぜ画像が表示されないのか

理由を知るには、Laravel のフォルダ構成を理解する必要があります。ポイントは2つのフォルダです。

フォルダ役割
public/ブラウザから直接見える場所。CSS や画像など公開ファイルを置く
storage/app/private/ など storage/ 配下ブラウザから直接は見えない場所。アップロードされたファイルを置く

Web サーバーは、セキュリティのために public/ フォルダだけを外部に公開しています。public/ の外にあるファイルは、URL を直接叩いても見られません。これはわざとそうしている仕組みです。データベースのパスワードが書かれた .env などが外から見えてしまったら大変ですから。

ところが、アップロードした画像は storage/app/public/ に保存されます。名前に public と付いていますが、場所は public/ フォルダの外です。

プロジェクト/
├── public/                  ← ブラウザから見える
│   └── (ここに無いと表示できない)
└── storage/
    └── app/
        └── public/          ← 画像はここに保存される(ブラウザから見えない)
            └── photo.jpg

つまり、保存はできているけれど、ブラウザから届かない場所にある。これが画像が表示されない理由です。

ではどうするか。画像を public/ にコピーする、という方法もありそうですが、それだとファイルが二重になってしまいます。

そこで Laravel は「シンボリックリンク」という仕組みを使います。

シンボリックリンクとは、**別の場所へつながっている「近道(ショートカット)」**のことです。Windows のショートカット、macOS のエイリアスと同じ考え方です。実体は1つのまま、別の場所からもたどれるようにします。

php artisan storage:link を実行すると、次のリンクが作られます。

public/storage  →(つながっている)→  storage/app/public

これで、ブラウザが public/storage/photo.jpg を見にくると、実際には storage/app/public/photo.jpg が返されます。ファイルをコピーせずに、公開できるようになるわけです。

実行してみる

プロジェクトのフォルダで実行します。

php artisan storage:link

成功すると、次のように表示されます。

INFO  The [public/storage] link has been connected to [storage/app/public].

これで完了です。ブラウザを再読み込みすると画像が表示されるはずです。

どこへリンクが作られるかは設定で決まっている

このコマンドが「どこからどこへ」リンクを作るかは、config/filesystems.php に書かれています。

'links' => [
    public_path('storage') => storage_path('app/public'),
],

これは「public/storage から storage/app/public へリンクを作る」という意味です。public_path()storage_path() は、それぞれのフォルダの場所を返す Laravel の関数です。

特別な理由がなければ、この設定はそのままで問題ありません。

ビューでの画像の表示方法

リンクを作ったら、画面に表示するときは asset() 関数を使います。

// コントローラ側: 画像を保存する
// storage/app/public/photos/ に保存され、"photos/xxx.jpg" のようなパスが返る
$path = $request->file('photo')->store('photos', 'public');

保存したパスをビュー(画面を作るファイル)で表示します。

<img src="{{ asset('storage/' . $photo->path) }}" alt="写真">

asset('storage/...')storage/ の部分が、さきほど作ったシンボリックリンクです。ここを忘れると表示されないので注意してください。

なお、Storage::url() を使う書き方もあり、こちらでも同じ URL が得られます。

<img src="{{ Storage::disk('public')->url($photo->path) }}" alt="写真">

よくあるつまずき

1. サーバーにデプロイしたら、また表示されなくなった

シンボリックリンクは環境ごとに作る必要があります。自分のパソコンで実行しても、公開サーバーには反映されません。

デプロイ(公開サーバーへ設置すること)したあと、サーバー側で改めて実行してください。

php artisan storage:link

多くのプロジェクトでは、デプロイ手順の中にこのコマンドを含めておきます。

2. 「already exists」と言われる

すでにリンクがある場合、次のように表示されます。

ERROR  The [public/storage] link already exists.

ERROR と出るので不安になりますが、これは「すでに近道があるので作らなかった」という意味です。リンクが正しく張れているなら、そのままで問題ありません。

壊れたリンクが残っていて作り直したい場合は、--force(フォース=強制)を付けます。

php artisan storage:link --force

これは既存のリンクを作り直すオプションです。

3. レンタルサーバーでシンボリックリンクが使えない

共用のレンタルサーバーなどでは、シンボリックリンクの作成が許可されていない場合があります。その場合は --relative(相対パスでリンクを作る)を試すと通ることがあります。

php artisan storage:link --relative

それでも通らない場合は、コントローラ経由でファイルを返す方法(Storage::download() などを使う)を検討します。

4. public/storage を Git に入れてしまっている

シンボリックリンクは環境ごとに作るものなので、Git(バージョン管理)に含める必要はありません。Laravel の標準の .gitignore では、はじめから除外されています。

まとめ

  • 画像が表示されないのは、保存先の storage/app/publicpublic/ の外にあり、ブラウザから直接見えないため。
  • php artisan storage:link は、public/storage から storage/app/public への**シンボリックリンク(近道)**を作るコマンド。
  • リンクの設定は config/filesystems.phplinks に書かれている。
  • 表示は asset('storage/' . パス) または Storage::disk('public')->url(パス) を使う。
  • デプロイ先でも実行が必要。すでにある場合の作り直しは --force、環境の制約があるときは --relative を試す。

ファイルのアップロード自体のやり方は、カリキュラムの フォームを作る で解説しています。