Laravel11.x, Laravel10.x, Laravel9.x, Laravel8.x, Laravel7.x, Laravel6.x, Laravel5.8, Laravel5.7で動作確認を行っています。ファイルのアップロード機能はLaravelのコア機能でバージョンによって手順が変わることはありません。バージョン毎に変わる手順ではないので、一度処理方法を理解することができれば長期にわたって使える知識になります。

本文書ではLaravel11.x, Laravel10.x, Laravel9.x, Laravel8.x, Laravel7.x, Laravel6.x, Laravel5.xを使ってファイルをアップロードする方法を中心に詳細を説明を行っていますがファイルのアップロード機能を使ったアプリケーションを開発する際の参考にできるようにアップロードだけではなく、アップロードしたファイルのブラウザからの閲覧方法についても説明を行なっています。

各バージョンのLaravelがインストール済みの状態で動作確認を行なっていきます。

アップロードフォームの作成

ブラウザ上からファイルをアップロードするためにはフォームの作成を行う必要があります。

ファイルのアップロードフォームをブラウザに表示させるまで以下の設定を行います。

  • web.phpへのルーティングの追加
  • コントローラーの作成
  • 入力フォームのBladeファイルの作成

フォームを表示、ファイルをアップロードするためのルーティングをweb.phpに追加します。


Route::resource('/upload', 'UploadController');

Laravel8以降は以下のようにルーティングの追加を行ってください。


//略
use App\Http\Controllers\UploadController;
//略

Route::resource('upload',UploadController::class);
古いLaravelバージョンで記述したルーティング方法でコントローラーの追加を行うとブラウザから/uploadにアクセスするとTarget class [UploadController] does not existエラーが発生します。このエラーが発生した場合はclassのインポート(use)を確認してください。
fukidashi

web.phpで指定したコントローラーUploadControllerをphp artisanコマンドを利用して作成します。


$ php artisan make:controller UploadController

   INFO  Controller [app/Http/Controllers/UploadController.php] created successfully.  

作成されるUploadController.phpにはメソッドが含まれていないのでindexメソッドを追加します。


<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UploadController extends Controller
{
    public function index(){

    	return view('index');
    }
}

ビューファイルの作成を行います。resource/viewsディレクトリの下にindex.blade.phpファイルを作成し、ファイルをアップロードするためのフォームを作成します。formタグのmethod属性にPOST、action属性にはPOSTリクエストの送信先である/uploadを指定します。

ファイルのアップロードを行う場合は、enctype=”multipart/form-data”は忘れずにform要素に設定をしてください。@crsfが利用できないバージョンの場合は{{ csrf_field() }}を利用してください。


<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>

	<form method="POST" action="/upload" enctype="multipart/form-data">

                @csrf

		{{-- {{ csrf_field() }} --}}

	<input type="file" id="file" name="file" class="form-control">

	<button type="submit">アップロード</button>

	</form>

</body>
</html>
csrfは
クロスサイトリクエストフォージェリ(CSRF)の略で、@csrfとも記述することが可能です。
fukidashi

ルーティングに設定した/uploadにアクセスするとファイルをアップロードするためのフォームが表示されます。

アップロードフォーム

アップロードフォーム

開発サーバを利用している場合は、ブラウザでアクセスする前にはphp artisan serveコマンドを実行して開発サーバを起動させておく必要があります。
fukidashi

アップロードしたフィルの情報確認

作成したフォームからファイルをアップロードして、アップロードしたファイルの情報を確認してみましょう。UploadController.phpファイルにstoreメソッドを追加します。


public function store(Request $request){
    dd($request->all());
}

storeメソッドを追加後、実際にファイルのアップロードを作成したフォームから行います。ここでは”アップロードファイル.pdf”という名前のファイルです。

dump&dieコマンド(dd)を実行するとアップロードしたファイルの情報を確認することができます。


array:2 [▼
  "_token" => "3bSrdIPLL3alM6iH3pYJIKRDEDXnZHcmWhOi3dCK"
  "file" => UploadedFile {#204 ▼
    -test: false
    -originalName: "アップロードファイル.pdf"
    -mimeType: "application/pdf"
    -error: 0
    #hashName: null
    path: "/private/var/folders/5h/bxvtcnj978dfmqwdkr59xvj40000gn/T"
    filename: "php3fAJXn"
    basename: "php3fAJXn"
    pathname: "/private/var/folders/5h/bxvtcnj978dfmqwdkr59xvj40000gn/T/php3fAJXn"
    extension: ""
    realPath: "/private/var/folders/5h/bxvtcnj978dfmqwdkr59xvj40000gn/T/php3fAJXn"
    aTime: 2018-12-31 02:23:19
    mTime: 2018-12-31 02:23:19
    cTime: 2018-12-31 02:23:19
    inode: 1084426
    size: 208312
    perms: 0100600
    owner: 501
    group: 20
    type: "file"
    writable: true
    readable: true
    executable: false
    file: true
    dir: false
    link: false
  }
]

ddコマンドでは、フォームから送られてくるすべての情報を表示していましたが、ファイルのみの情報を表示させるためにfileメソッドを利用します。


 dd($request->file('file'));
fileメソッドの中のfileという文字列はフォームのinput要素のnameをfileに設定しているためです。input要素のnameをpdfに変更するとfile(‘pdf’)に変更する必要があります。
fukidashi

fileメソッドを利用するとmime typeやサイズの情報も確認することができます。ファイルの情報は、UploadFileインスタンスに保存されていることも下記の情報からわかります。


UploadedFile {#204 ▼
  -test: false
  -originalName: "アップロードファイル.pdf"
  -mimeType: "application/pdf"
  -error: 0
  #hashName: null
  path: "/private/var/folders/5h/bxvtcnj978dfmqwdkr59xvj40000gn/T"
  filename: "php01V3en"
  basename: "php01V3en"
  pathname: "/private/var/folders/5h/bxvtcnj978dfmqwdkr59xvj40000gn/T/php01V3en"
  extension: ""
  realPath: "/private/var/folders/5h/bxvtcnj978dfmqwdkr59xvj40000gn/T/php01V3en"
  aTime: 2018-12-31 02:26:38
  mTime: 2018-12-31 02:26:38
  cTime: 2018-12-31 02:26:38
  inode: 1084476
  size: 208312
  perms: 0100600
  owner: 501
  group: 20
  type: "file"
  writable: true
  readable: true
  executable: false
  file: true
  dir: false
  link: false
}

もしformタグにenctype=”multipart/form-data”の設定を忘れた場合はファイルをアップロードするとファイル名のみ表示されます。

アップロードしたファイルの保存

アップロードしたファイル情報を保持するUploadFileクラスはファイルを保存するためのstoreメソッドを持っています。先ほどdd関数で表示されたUploadFileインスタンスのstoreメソッドを利用することで、サーバ上にアップロードしたファイルを保存することができます。ファイルが保存される場所はLaravelインストールディレクトリの下にあるstorage/appです。


public function store(Request $request){

   $request->file('file')->store('');
		
}
storeメソッドにはパスを設定することができます。もしstore(‘test’)を設定するとstorage/appの下にtestディレクトリが作成されそこにファイルが保存されます。
fukidashi

サーバ上でファイルが保存されているか確認します。ファイル名は、Laravel側で自動で付与されます。Laravelではたったこれだけの処理で簡単にサーバへのファイルのアップロードを行うことができます。


% pwd
/Users/mac/Desktop/laravel_upload/storage/app
% ls
1bRLVYORlvI5curaPFUVob8DVMUBpSL1D2zJcXFS.pdf
public

任意の名前をつけてファイルを保存

アップロードしたファイル名または任意の名前を付けたい場合は、storeAsメソッドを利用します。


$request->file('file')->storeAs('','upload_file.pdf');

storeAsメソッドで指定したファイル名で保存されることが確認できます。


% pwd
/Users/mac/Desktop/laravel_upload/storage/app
% ls
1bRLVYORlvI5curaPFUVob8DVMUBpSL1D2zJcXFS.pdf
public
upload_file.pdf

アップロードしたファイル名をつけて保存

UploadFileインスタンスのgetClientOriginalNameメソッドを利用すると拡張子を含め、アップロードしたファイルのファイル名を取得することができます。


$file_name = $request->file('file')->getClientOriginalName();

$request->file('file')->storeAs('',$file_name);

ここではアップロードファイル.pdfという名前のファイルをアップロードしたのでアップロードファイル.pdfとしてファイルが保存されています。日本語ファイル名も文字化けされることなく問題なく保存されることも確認できます。


% pwd
/Users/mac/Desktop/laravel_upload/storage/app
% ls
1bRLVYORlvI5curaPFUVob8DVMUBpSL1D2zJcXFS.pdf
public
upload_file.pdf
アップロードファイル.pdf
storeメソッドもstoreAsメソッドも戻り値は保存したファイルの相対パスです。storage/appからの相対パスになるのでstoreAsの第一引数に何も指定しない場合はファイル名となります。
fukidashi

複数ファイルのアップロード

ここまでは1つのファイルのアップロード処理のみを行ってきましたが、複数のファイルのアップロードの処理を確認します。

複数ファイルのアップロードには2つの方法があります。

  1. 1つのフォームで複数のinput要素を設定する方法
  2. 一つのinput要素で複数のファイルをアップロードする方法

1つ目の方法はこれまでの方法と同じなので、ここでは2つ目の方法を説明します。

1つ目の場合はinput要素をフォームに複数用意して、要素毎にnameの異なる名前を設定します。異なる名前に設定することでアップロードした後のstoreメソッドの中でname毎にファイルを取り出して個別に処理を行うことができます。
fukidashi

複数ファイルのアップロードを許可するためには、input要素の変更が必要です。nameをfile[]に変更し、multipleを追加しています。


<input type="file" id="file" name="file[]" class="form-control" multiple>

input要素にmultipleを追加するとファイル選択時に複数のファイルが選択可能となります。1つのファイルではなく複数のファイルを選択してください。

UploadController.phpファイルでアップロードされたファイルをddで確認します。


dd($request->file('file'));

フォームから2つのファイルを同時にアップすると下記のように配列に含まれたファイル情報が表示されます。


array:2 [▼
  0 => UploadedFile {#206 ▶}
  1 => UploadedFile {#212 ▶}
]

配列に保存されたファイルは、foreachで展開することで個別にファイルの保存を行うことができます。


$files = $request->file('file');

foreach($files as $file){

	$file_name = $file->getClientOriginalName();

	$file->storeAS('',$file_name);

}

1つのファイルでのアップロード方法から複数のファイルでのアップロード方法まで理解することができました。次はアップロードしたファイルのブラウザからアクセス方法を確認していきます。

アップロードしたファイルの閲覧

シンボリックリンクの作成

ファイルをアップロードしただけではブラウザからアップロードしたファイルを閲覧することはできません。ブラウザからサーバ上のファイルにアクセスするためには、Laravelインストールディレクトリの下にある公開用のpublicディレクトリの下に保存する必要があります。しかし、アップロードしたファイルは/storage/appの下にあるためアクセスすることができません。そのため/publicディレクトリと/storage/appディレクトリとの間でリンクを持たせる必要があります。リンクを貼ることで/publicにアクセスすると/strorage/appディレクトリにアクセスすることができるようになります。Windowsで言えばショートカットをイメージしてください。

Laravelではリンクを貼る設定を行う機能も備えています。

php artisan storage:linkを実行します。

表示されるパスは環境によって異なりますがLaravel9では実行すると以下のメッセージが表示されます。どの場所とどの場所でリンクが貼られているのかはっきり理解することができます。


$ php artisan storage:link
//Laravel11
   INFO  The [public/storage] link has been connected to [storage/app/public].  
//Laravel9
The [/Users/mac/Desktop/laravel_upload/public/storage] link has been connected to [/Users/mac/Desktop/laravel_upload/storage/app/public].
The links have been created.

/publicディレクトリの下にstorageディレクトリが作成され、/storage/app/publicへシンボリックリンクが張られます。


$ pwd
/Users/mac/Desktop/laravel_upload/public
$ ls -l storage
lrwxr-xr-x  1 mac  staff  47 12 31 11:55 storage ->/Users/mac/Desktop/laravel_upload/storage/app/public
シンボリックリンクが張られることにより、/storage/app/publicの下にupload.pdfファイルを保存すると/public/storageからupload.pdfにアクセス可能となります。
fukidashi

ファイルの保存

本文書通りに動作確認を行なっているとファイルのアップロード設定が複数ファイル対応になっています。ここでは最初に説明を行なった1つのファイルのアップロード方法に戻してください。


<input type="file" id="file" name="file" class="form-control">

/storage/app/publicの下にアップロードしたファイルを保存します。ファイルを保存するためにUploadController.phpのstorsAsメソッドの変更を行います。


$file_name = $request->file('file')->getClientOriginalName();

$request->file('file')->storeAs('public',$file_name);
storeAsメソッドでは第一引数に何も設定しないとstorage/appディレクトリにファイルが保存されるため、publicを設定するとstorage/app/publicにファイルが保存されます。
fukidashi

ファイルがstorage/app/publicディレクトリの下に保存されていることが確認できます。表示されるファイル名はアップロードしたファイルによって異なります。


$ pwd
/Users/mac/Desktop/laravel_upload/storage/app/public
$ ls
アップロードファイル.pdf

シンボリックリンクが設定されているため、public/storage/ディレクトリからもアップロードファイル.pdfファイルへアクセスすることができます。


mac$ pwd
/Users/mac/Desktop/laravel_upload/public/storage
mac$ ls
アップロードファイル.pdf

ブラウザからのファイルの閲覧

publicディレクトリに下に置いたファイルは、ブラウザからアクセスできるため、保存したファイルを閲覧してみましょう。index.blade.phpのビューファイルに下記の行を追加します。ここではアップロードファイル.pdfとしていますが各自がアップロードしたファイルを指定してください。画像ファイルをアップロードした場合はimgタグを利用してください。


<a href="/storage/アップロードファイル.pdf">アップロードファイル</a>

ブラウザで下記のように表示されます。

アップロードファイルのリンク表示

アップロードファイルのリンク表示

リンク部分をクリックするとPDFの内容がブラウザに表示されます。

リンクからPDFファイルの中身を確認

リンクからPDFファイルの中身を確認

Storageを使用したファイルの保存

LaravelではStorageファサードを使用してファイルの保存を行うこともできます。

storeメソッドを使用して下記のようにファイルの保存ができることを説明しました。


public function store(Request $request){

   $request->file('file')->store('');
		
}

ここからは同様の処理をStorageファサードを利用して行なっていきます。

Storageファサードの使い方については下記の文書でより詳細に解説しています。Amazon Web ServiceのS3にファイルを保存したい場合についても説明を行なっています。

putFileメソッドによる保存

Storageファサードを使用するとuseでStorageを呼び出す必要があります。下記のようにStorageファサードを利用するとstoreメソッドと同様putFileメソッドでファイルの保存が可能です。下記の場合は/strage/appディレクトリにアップロードしたファイルが保存されます。


namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{

    public function index(){

    	return view('index');
    }

    public function store(Request $request){
 
    	Storage::putFile('',$request->file('file'));

    }  	
}

putFileAsメソッドによる保存

名前をつけたい場合は、putFileAsメソッドを使用します。/storage/appディレクトリにアップロードしたファイル名で保存されます。


$file_name = $request->file('file')->getClientOriginalName();

Storage::putFileAs('',$request->file('file'), $file_name);

putによる保存

putメソッドによってもファイルの保存を行うことができます。file_get_contents関数を使い文字列としてファイル内容の読み込みを行っています。


$file_name = $request->file('file')->getClientOriginalName();

Storage::put($file_name, file_get_contents($request->file('file')->getRealPath()));

上記の文字列を保存とファイルの保存の関係がわかりにくい場合は下記の操作を見ればはっきり理解できるかと思います。


Storage::put('file.txt', 'この文字列をファイルに追加');

”この文字列をファイルに追加”という内容のfile.txtファイルが作成されます。

非公開のファイルのダウンロード

storage/app/public以下に保存したファイルに対してはpublicが公開フォルダであるためブラウザからアクセスすることができます。ブラウザでは直接アクセスできないapp以下のファイルをダウンロードしたいという場合は下記の方法で行うことができます。

事前にアップロードしたファイルをapp/storage/privateディレクトリの下にprofile.pngという名前で保存しています。
fukidashi

公開ディレクトリに保存されていないファイルは、Storage::downloadでファイルのダウンロードを行うことができます。


$filePath = 'private/profile.png';

$fileName = 'profile.png';

$mimeType = Storage::mimeType('private/profile.png');

$headers = [['Content-Type' => $mimeType]];

return Storage::download($filePath, $fileName, $headers);

ダウンロード方法はいくつかあるので、下記の文書にまとめています。

その他

ファイル名と拡張子の取得

ファイルの拡張子はextensionメソッドで取得できます。そのほかにguessExtensionメソッドやgetClientOriginalExtensionメソッドでも可能です。

拡張子を取得するだけでもさまざまなメソッドがあります。どのようなメソッドが存在するかはUploadFileクラスのソースコードを見るとわかります。ぜひ興味がある人は一度確認してみてください。

  • vendor/laravel/framework/src/Illuminate/Http/UploadedFile.php
  • symfony/http-foundation/File/File.php

pathinfo

アップロードしたファイルのファイル名と拡張子を別々に取得したい場合はpathinfo関数を利用することもできます。pathinfoは内部で利用されています。


$file= $request->file('file')->getClientOriginalName();

$filename = pathinfo($file, PATHINFO_FILENAME);//ファイル名のみ

$extension = pathinfo($file, PATHINFO_EXTENSION);//拡張子のみ

ここまでの手順を理解することができればLaravelアプリケーションにファイルのアップロード機能を実装することができます。