How to Upload Multiple Files in Laravel in the best way?

Application development often requires to upload multiple files at the same time. This feature is supported by Laravel applications. In the previous tutorials, we discussed how to upload single file in Laravel. So, let us now look at a step by step example to upload multiple files in Laravel.

Step 1: Install and Create new Project

Firstly, we will install Laravel 9 and create a new project. We will have a simple form with a file input field. You can use it to select multiple images. Prior to submitting the form we will store those images in a folder and database.

composer create-project laravel/laravel upload_multiple_files

Step 2: Create new Migrations

Secondly, to store multiple images and their paths in a single place, we will need a separate table to handle it. Therefore, we will create migrations for a new images table.

php artisan make:migration create_new_files_table

The resulting migrations file will look as shown below.

    public function up()
    {
        Schema::create('files', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }
  
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('files');
    }
};

The details above have the up() function to create a new table ‘files’ with the three fields specified in the create function. The down() function drops the ‘files’ table, if it exists in database. After adding the details as above, we will run the migrations.

php artisan migrate

Step 3: Create new Modal

Next step, we create an Files Modal.

php artisan make:model Images

The Modal will come under the app/Models directory and has the code as shown below.

<?php
  
namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
  
class Files extends Model
{
    use HasFactory;
  
    protected $fillable = [
        'name'
    ];
}

Step 4: Create new Routes

New routes in the routes/web.php file are created to link the Controller functions and blade views.

Route::controller(FilesController::class)->group(function(){
    Route::get('files-upload', 'index');
    Route::post('files-upload', 'save')->name('files.save');
});

Step 5: Create new Controller

Now we create a controller that handles retrieving the files and uploading to the database.

php artisan make:controller FilesController

We will then populate the Controller file with the code as shown below.

<?php
  
namespace App\Http\Controllers;
  
use Illuminate\Http\Request;
use App\Models\File;
  
class FileController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('filesUpload');
    }
    
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function save(Request $request)
    {
        $request->validate([
            'files' => 'required',
            'files.*' => 'required|mimes:pdf,xlx,csv|max:2048',
        ]);
      
        $files = [];
        if ($request->file('files')){
            foreach($request->file('files') as $key => $file)
            {
                $fileName = time().rand(1,99).'.'.$file->extension();  
                $file->move(public_path('uploads'), $fileName);
                $files[]['name'] = $fileName;
            }
        }
  
        foreach ($files as $key => $file) {
            File::create($file);
        }
     
        return back()
                ->with('success','You have successfully upload file.');
   
    }
}

The index() function returns the view where a user can upload files. Files data from the view is processed in the save function. After validation, a file array processes the multiple files added using a foreach loop. Therefore, each file is moved to the uploads folder in the public path defined. For example, this can be the public path or an file storage folder. Locations of both paths are stated below. Consequently, a success message is returned if all steps go smoothly.

Storing Files in Storage Folder

$file->storeAs('files', $fileName);
// storage/app/files/file.png

Storing Files in Public Folder

$file->move(public_path('files'), $fileName);
// public/files/file.png

Step 6: Create new Blade file

Finally, the last step will be to create a blade file. This will make a user view the multi-files select and upload option.

<!DOCTYPE html>
<html>
<head>
    <title>Laravel 9 Upload Multiple Files Example - scratchcoding.dev</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
      
<body>
<div class="container">
       
    <div class="panel panel-primary">
  
      <div class="panel-heading">
        <h2>Laravel 9 Upload Multiple Files Example - scratchcoding.dev</h2>
      </div>
  
      <div class="panel-body">
       
        @if ($message = Session::get('success'))
            <div class="alert alert-success alert-block">
                <strong>{{ $message }}</strong>
            </div>
        @endif
      
        <form action="{{ route('files.save') }}" method="POST" enctype="multipart/form-data">
            @csrf
  
            <div class="mb-3">
                <label class="form-label" for="inputFile">Select Files:</label>
                <input 
                    type="file" 
                    name="files[]" 
                    id="inputFile"
                    multiple 
                    class="form-control @error('files') is-invalid @enderror">
  
                @error('files')
                    <span class="text-danger">{{ $message }}</span>
                @enderror
            </div>
   
            <div class="mb-3">
                <button type="submit" class="btn btn-success">Upload</button>
            </div>
       
        </form>
      
      </div>
    </div>
</div>
</body>
    
</html>

The blade file above views the upload files option to the user. In addition, it has two separate divs that pop up required messages. One for success and one for error.