Skip to main content
Laravel Basics Tutorial
CHAPTER 14 Beginner

File Uploads and Storage in Laravel

Updated: May 14, 2026
25 min read

# CHAPTER 14

File Uploads and Storage in Laravel

1. Introduction

Handling file uploads in core PHP is notoriously painful. You have to deal with the messy $_FILES superglobal, manually check MIME types, and write clunky move_uploaded_file() functions. Laravel provides a powerful, elegant abstraction over file storage called the Filesystem. Whether you are storing a profile picture on your local server or uploading a massive video directly to Amazon S3 cloud storage, Laravel's API remains exactly the same.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Configure a Blade form for multipart file uploads.
  • Capture and validate file uploads in a Controller.
  • Store files securely on the local server.
  • Create a symbolic link to make uploaded files publicly accessible.

3. Beginner-Friendly Explanation

Imagine a post office. In old PHP, you had to personally grab the package from the customer, weigh it, inspect the contents, drive it to the warehouse, and put it on a specific shelf. In Laravel, you just tell the Storage Assistant: *"Take this package, check if it's an image under 2MB, and put it in the 'avatars' aisle."* Laravel instantly hashes a unique, random filename for security, saves the file to the hard drive, and hands you a piece of paper (a text string) with the exact location. You take that piece of paper and save it in your MySQL database.

4. Step 1: The Blade Form

To upload files, the HTML <form> MUST have the enctype="multipart/form-data" attribute. Without it, the browser strips the file away and only sends text.

resources/views/upload.blade.php

html
1234567
<form action="/upload-avatar" method="POST" enctype="multipart/form-data">
    @csrf
    <label>Profile Picture:</label>
    <input type="file" name="avatar">
    
    <button type="submit">Upload</button>
</form>

5. Step 2: Validating and Storing in the Controller

When the form is submitted, the file arrives via the Request object. We validate it, and then use the store() method.

app/Http/Controllers/UserController.php

php
1234567891011121314151617181920212223242526
use Illuminate\Http\Request;
use App\Models\User;

public function storeAvatar(Request $request)
{
    // 1. Validation (CRITICAL for security)
    $request->validate([
        &#039;avatar' => 'required|image|mimes:jpeg,png,jpg|max:2048', // Max 2MB
    ]);

    // 2. Check if the file is actually there
    if ($request->hasFile(&#039;avatar')) {
        
        // 3. Store the file. 
        // This saves it to storage/app/public/avatars/ and returns the path!
        $path = $request->file(&#039;avatar')->store('avatars', 'public');

        // 4. Save the text path to the database
        // e.g., $path will equal: "avatars/xYz123RandomHash.jpg"
        $user = auth()->user();
        $user->profile_image = $path;
        $user->save();

        return back()->with(&#039;success', 'Avatar uploaded successfully!');
    }
}
Where did Laravel physically put the file? By default, Laravel stores it deep inside storage/app/public/avatars/. However, the internet is only allowed to see files inside your public/ directory! If you try to put an image tag like <img src="/storage/avatars/image.jpg">, the browser will show a broken image. We must create a shortcut (Symbolic Link) that connects the hidden storage folder to the public internet folder.

Run this Artisan command ONE TIME in your terminal:

bash
1
php artisan storage:link

This creates a magic tunnel. Now, files saved in storage/app/public are instantly accessible via the public/storage URL.

7. Displaying the Uploaded File in Blade

Now that the database holds the path (e.g., avatars/xyz.jpg), and the symlink is created, we use the asset() helper function to generate the correct URL.
html
12
<!-- The asset() function generates a full URL to the public folder -->
<img src="{{ asset(&#039;storage/' . Auth::user()->profile_image) }}" alt="Avatar">

8. Cloud Storage (Amazon S3)

The magic of Laravel's Filesystem is that you can switch to Cloud Storage without changing your Controller code. If your website grows and you want to store images on Amazon S3 instead of your local server, you just change one line in your .env file (FILESYSTEM_DISK=s3). The $request->file('avatar')->store() command will automatically upload the file to Amazon servers!

9. Best Practices

  • Never Store Original Filenames: Laravel's store() method automatically generates a unique 40-character hashed filename (e.g., a7b9c...jpg). Always use this. If you let users keep their original filename, a user could upload a file named index.php and trick your server into executing malicious code.

10. Common Mistakes

  • Forgetting enctype: If your Controller says $request->hasFile('avatar') is always returning false, go check your HTML. You forgot enctype="multipart/form-data" in the <form> tag.

11. Exercises

  1. 1. Explain the purpose of the php artisan storage:link command. Why can't the internet see files directly in the storage/ directory?

12. Coding Challenges

  • Challenge: Write the Controller validation rules for a PDF document upload. The file must be required, it must be exactly a PDF (mimes:pdf), and it must not exceed 5 Megabytes in size.

13. MCQs with Answers

Question 1

Which HTML attribute is absolutely required on a <form> tag to allow file uploads to a Laravel backend?

Question 2

When Laravel's store('avatars', 'public') method successfully saves a file to the hard drive, what does the method return to the developer to be saved in the MySQL database?

14. Interview Questions

  • Q: Describe the complete workflow of handling an image upload in Laravel, from the Blade form attributes, to Controller validation, to database storage, and finally displaying it via a symbolic link.
  • Q: Why is it recommended to store file paths in a database rather than storing the binary file payload directly in a database blob column?

15. FAQs

Q: Can I delete a file from the server when a user deletes their account? A: Yes! You use the Storage facade: Storage::disk('public')->delete($user->profile_image);. This erases the physical file from the hard drive to save space.

16. Summary

In Chapter 14, we handled physical assets. By modifying our forms to accept multipart data, validating the file streams via the Request object, and utilizing Laravel's store() method, we securely handled user uploads. We learned the critical importance of the storage:link command to bridge our secure internal storage with the public-facing internet, allowing us to serve dynamic, user-uploaded content effortlessly.

17. Next Chapter Recommendation

We have mastered building websites that return HTML Views to human browsers. But what if a mobile app needs our data? Proceed to Chapter 15: Building REST APIs with Laravel.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·