How to Upload Multiple Files in Laravel

File upload is one of the common needs in web applications. Laravel provides an easy and secure way to handle file uploads, including uploading multiple files at once. In this tuto...

How to Upload Multiple Files in Laravel

File upload is one of the common needs in web applications. Laravel provides an easy and secure way to handle file uploads, including uploading multiple files at once. In this tutorial, you'll learn how to build a multiple file upload feature in Laravel from scratch, complete with validation and storage.

Storage Preparation

Laravel uses a filesystem to manage files. First, make sure you've created the symbolic link so uploaded files can be accessed publicly:

php artisan storage:link

This command creates a link from public/storage to storage/app/public. Files stored in storage/app/public/ will be accessible via URL.

Creating the Migration and Model

Create a table to store the uploaded file data:

php artisan make:model UploadedFile -m

Edit the migration file:

public function up(): void
{
    Schema::create('uploaded_files', function (Blueprint $table) {
        $table->id();
        $table->string('original_name');
        $table->string('stored_name');
        $table->string('path');
        $table->string('mime_type');
        $table->unsignedBigInteger('size');
        $table->timestamps();
    });
}

Edit the model app/Models/UploadedFile.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class UploadedFile extends Model
{
    protected $fillable = [
        'original_name',
        'stored_name',
        'path',
        'mime_type',
        'size',
    ];
}

Run the migration:

php artisan migrate

Creating the HTML Form

Create the upload form view. Note the multiple and enctype attributes:

<!-- resources/views/upload/index.blade.php -->
<form action="{{ route('upload.store') }}" method="POST" enctype="multipart/form-data">
    @csrf
    <div>
        <label for="files">Choose files (more than one allowed):</label>
        <input type="file" name="files[]" id="files" multiple accept="image/*,.pdf,.doc,.docx">
        @error('files.*')
            <p style="color:red">{{ $message }}</p>
        @enderror
    </div>
    <button type="submit">Upload</button>
</form>

The important key: the input name must be files[] (with square brackets) so PHP recognizes it as an array.

Creating the Controller

php artisan make:controller UploadController

Edit app/Http/Controllers/UploadController.php:

<?php

namespace App\Http\Controllers;

use App\Models\UploadedFile;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{
    public function index()
    {
        $files = UploadedFile::latest()->get();
        return view('upload.index', compact('files'));
    }

    public function store(Request $request)
    {
        $request->validate([
            'files'   => 'required|array|min:1|max:10',
            'files.*' => 'file|mimes:jpeg,png,jpg,gif,pdf,doc,docx|max:2048',
        ]);

        $uploadedFiles = [];

        foreach ($request->file('files') as $file) {
            // Store in storage/app/public/uploads
            $path = $file->store('uploads', 'public');

            $uploadedFiles[] = UploadedFile::create([
                'original_name' => $file->getClientOriginalName(),
                'stored_name'   => basename($path),
                'path'          => $path,
                'mime_type'     => $file->getMimeType(),
                'size'          => $file->getSize(),
            ]);
        }

        return redirect()->route('upload.index')
            ->with('success', count($uploadedFiles) . ' files uploaded successfully.');
    }

    public function destroy(UploadedFile $uploadedFile)
    {
        // Delete the file from storage
        Storage::disk('public')->delete($uploadedFile->path);
        $uploadedFile->delete();

        return redirect()->route('upload.index')
            ->with('success', 'File deleted successfully.');
    }
}

Defining the Routes

// routes/web.php
use App\Http\Controllers\UploadController;

Route::get('/upload', [UploadController::class, 'index'])->name('upload.index');
Route::post('/upload', [UploadController::class, 'store'])->name('upload.store');
Route::delete('/upload/{uploadedFile}', [UploadController::class, 'destroy'])->name('upload.destroy');

Displaying the Uploaded Files

Add this section in the view to display the file list:

@if(session('success'))
    <p style="color:green">{{ session('success') }}</p>
@endif

<ul>
    @foreach($files as $file)
    <li>
        @if(str_starts_with($file->mime_type, 'image/'))
            <img src="{{ Storage::url($file->path) }}" width="100">
        @endif
        {{ $file->original_name }}
        ({{ number_format($file->size / 1024, 2) }} KB)
        <form action="{{ route('upload.destroy', $file) }}" method="POST" style="display:inline">
            @csrf @method('DELETE')
            <button type="submit">Delete</button>
        </form>
    </li>
    @endforeach
</ul>

File Upload Security Tips

  • Always validate the file type with mimes and limit the size with max.
  • Use the random file name Laravel generates, not the original name from the user.
  • Don't store executable files in a publicly accessible folder.
  • Set the upload_max_filesize and post_max_size limits in php.ini as needed.

Conclusion

Building a multiple file upload feature in Laravel is quite easy thanks to built-in helpers like $request->file(), store(), and the Storage system. With proper validation and secure storage, your upload feature is ready for production. You can develop it further by adding an upload progress bar with JavaScript or integrating with a cloud service like AWS S3.

upload multiple file laravel laravel file upload laravel storage upload gambar laravel multiple upload php laravel validation file
Share this article
Back to Blog
🚀 Partner Recommendation

Need Premium Source Code & Business Apps?

Access Laravel applications, POS systems, School Management, Clinic Software, ERP solutions, and ready-to-use premium source code at GudangCode.

GudangCode
  • ✔ Premium Source Code
  • ✔ Ready-to-Use Systems
  • ✔ Lifetime Updates
  • ✔ Lifetime Membership
  • ✔ Daily App Updates
Join Membership →