Laravel File Uploads and Storage

Lesson 30 of 50 - Learn how to upload, validate, store, display, and delete files in Laravel.

60%
1. What Is File Uploading in Laravel?

File uploading allows users to send files from their computer to a Laravel application. Examples include profile photos, student documents, assignment files, and PDF certificates.

Laravel provides tools for:

  • Validating uploaded files
  • Storing files on local or public disks
  • Generating file paths
  • Displaying uploaded files
  • Deleting files when they are no longer needed

Important:

An HTML upload form must use enctype="multipart/form-data". Without it, the browser will not submit uploaded files correctly.

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

    <input type="file" name="document">

    <button type="submit">Upload File</button>

</form>
2. Create the Upload Route and Controller

Create a controller using Artisan:

php artisan make:controller FileUploadController

Open routes/web.php and add:

use App\Http\Controllers\FileUploadController;
use Illuminate\Support\Facades\Route;

Route::get('/upload', [FileUploadController::class, 'create'])
    ->name('files.create');

Route::post('/upload', [FileUploadController::class, 'store'])
    ->name('files.store');

The GET route displays the upload form. The POST route receives and processes the uploaded file.

These routes normally use Laravel's web middleware, which provides CSRF protection for the POST request.

3. Create the File Upload Form

Create the file resources/views/upload.blade.php.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Upload File</title>
</head>
<body>

    <h2>Upload a File</h2>

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

    @if ($errors->any())
        <ul style="color: red;">
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    @endif

    <form action="{{ route('files.store') }}"
          method="POST"
          enctype="multipart/form-data">

        @csrf

        <label for="document">Choose a file:</label>
        <input type="file" name="document" id="document" required>

        <button type="submit">Upload</button>

    </form>

</body>
</html>

The @csrf directive adds a CSRF token to the form. The multipart/form-data encoding allows the browser to submit the file.

4. Validate and Store an Uploaded File

Open app/Http/Controllers/FileUploadController.php. Add the following code:

<?php

namespace App\Http\Controllers;

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

class FileUploadController extends Controller
{
    public function create()
    {
        return view('upload');
    }

    public function store(Request $request)
    {
        $request->validate([
            'document' => [
                'required',
                'file',
                'mimes:pdf,jpg,jpeg,png,doc,docx',
                'max:2048',
            ],
        ]);

        $path = $request->file('document')
            ->store('documents', 'public');

        return redirect()
            ->route('files.create')
            ->with('success', 'File uploaded successfully!');
    }
}

Explanation:

  • required means a file must be selected.
  • file verifies that the uploaded input is a file.
  • mimes restricts allowed file types.
  • max:2048 limits the file to 2048 KB, or 2 MB.
  • store() saves the file and returns its storage path.

The file is stored on Laravel's public disk inside storage/app/public/documents when using the standard local public-disk configuration.

The $path variable contains the stored relative path. Save it in a database if you need to retrieve the file later.

5. Make Uploaded Files Accessible

To make files on Laravel's public disk accessible through the web server, run:

php artisan storage:link

This normally creates a symbolic link from public/storage to storage/app/public.

For example, a file stored at documents/example.pdf can be accessed through the public storage URL:

/storage/documents/example.pdf

Generate the URL in Laravel:

use Illuminate\Support\Facades\Storage;

$url = Storage::disk('public')
    ->url('documents/example.pdf');

You can display a public image in Blade:

<img
    src="{{ Storage::disk('public')->url($path) }}"
    alt="Uploaded image"
    width="200"
>

Make sure Storage is imported in PHP code where required. Files on a public disk should only contain content intended for public access.

6. Store the File Path in a Database

In real applications, you usually save the file path in a database so the uploaded file can be associated with a student, user, or document record.

For this example, assume a documents table has title and file_path columns.

Example migration:

Schema::create('documents', function ($table) {
    $table->id();
    $table->string('title');
    $table->string('file_path');
    $table->timestamps();
});

Add this import when using the migration:

use Illuminate\Support\Facades\Schema;

Create a Document model:

php artisan make:model Document

In app/Models/Document.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Document extends Model
{
    protected $fillable = [
        'title',
        'file_path',
    ];
}

Save the uploaded file and its path:

use App\Models\Document;
use Illuminate\Http\Request;

public function store(Request $request)
{
    $validated = $request->validate([
        'title' => ['required', 'string', 'max:255'],
        'document' => [
            'required',
            'file',
            'mimes:pdf,jpg,jpeg,png,doc,docx',
            'max:2048',
        ],
    ]);

    $path = $request->file('document')
        ->store('documents', 'public');

    Document::create([
        'title' => $validated['title'],
        'file_path' => $path,
    ]);

    return redirect()
        ->route('files.create')
        ->with('success', 'Document saved successfully!');
}

Add a title input with name="title" to your form for this version of the controller. Run the migration before using this example.

7. Retrieve and Display Uploaded Files

You can retrieve the saved document records from the database and display links to their files.

Controller example:

use App\Models\Document;

public function index()
{
    $documents = Document::latest()->paginate(10);

    return view('documents.index', compact('documents'));
}

Blade example:

<h2>Uploaded Documents</h2>

@forelse ($documents as $document)
    <div>
        <h4>{{ $document->title }}</h4>

        <a
            href="{{ Storage::disk('public')->url($document->file_path) }}"
            target="_blank"
            rel="noopener"
        >
            View File
        </a>
    </div>
@empty
    <p>No documents uploaded yet.</p>
@endforelse

{{ $documents->links() }}

Import the Storage facade in the Blade file if you use it there:

@php
    use Illuminate\Support\Facades\Storage;
@endphp

Alternatively, generate file URLs in the controller and pass them to the view. Only generate public URLs for files intended to be publicly accessible.

8. Delete an Uploaded File

Laravel's Storage facade provides a method for deleting stored files.

Delete a file from the public disk:

use Illuminate\Support\Facades\Storage;

Storage::disk('public')->delete(
    'documents/example.pdf'
);

Delete a document and its stored file:

use App\Models\Document;
use Illuminate\Support\Facades\Storage;

public function destroy(Document $document)
{
    Storage::disk('public')->delete($document->file_path);

    $document->delete();

    return redirect()
        ->back()
        ->with('success', 'Document deleted successfully!');
}

Define a route for deletion:

Route::delete(
    '/documents/{document}',
    [DocumentController::class, 'destroy']
)->name('documents.destroy');

The controller method assumes route-model binding is configured by the parameter name and that the Document model exists.

Security reminders:

  • Authorize the user before deleting documents.
  • Validate uploaded files on the server.
  • Do not trust the original filename or extension alone.
  • Keep sensitive files on a private disk and authorize downloads.
  • Handle missing files and storage failures where appropriate.
9. Practical Assignment

Create a document upload module for a student management system.

  1. Create a documents table with title and file_path columns.
  2. Create a Document model.
  3. Create a controller for uploading documents.
  4. Create a form with a title field and file input.
  5. Validate file type and file size.
  6. Store uploaded files on the public disk.
  7. Save the stored file path in the database.
  8. Display uploaded documents in a table.
  9. Add a link to view or download each file.
  10. Add a delete button with authorization checks.
  11. Display success and validation messages.
  12. Paginate the document listing.

Challenge:

Build a student document upload page that accepts PDF certificates and JPG or PNG profile photos. Store the student ID with each document, validate the upload, and ensure students can access only documents they are authorized to view.

Remember to run php artisan storage:link when using Laravel's public local disk, and configure private storage for sensitive student documents.

Quick Quiz

1. Which form encoding is required for file uploads?




2. Which Laravel method can store an uploaded file?




3. Which command creates the public storage symbolic link?