How to Upload images in Laravel 7

MacTavish
5 min readMay 3, 2020

Reading the documentation for File uploading seems a bit confusing to newbies. It happened with me too but I sorted it out and now you will sort it out too. By the end of this tutorial, you will have a Laravel application like the one shown below.

Image Upload Laravel Application

TL;DR: If you just want to know the how to handle image on back-end then scroll down to the “Controller” section. it’s just one small and simple controller. If you just want to try it out and want the working code then clone the GitHub repository.

Assuming you have Laravel installed and now you want to enable your users to upload images but…. how to do that. Well, image upload can be broken down into some simple steps.

  1. Create a symlink.
  2. Check if the image is present in request.
  3. get the image and validate it.
  4. Pass your request through validation rules. You might want to set a max size or file type restriction and that’s the place to do it.
  5. Store the image.
  6. Generate a url and store it in DB if you have to display the image to the user later.

Now you know the process precisely, it’s time to dive into code.

Model

For sake of this tutorial, we will use a very simple Model “File”. This model will only contain two fillable attributes. “name” and “url”.

<?php

// I have placed my model in app\Models. Change the namespace to below if you want to keep it in default location.
//namespace App;
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class File extends Model
{
//
protected $fillable = [
'name', 'url',
];
}

Migration

Let’s create a migration for our Model.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateFilesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('files', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 40);
$table->string('url', 80);
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('files');
}
}

Routes

Let’s make some routes before we start with views.

<?php

/*
Welcome view has been edited.
*/

Route::get('/', function () {
return view('welcome');
});

Route::view('/file-upload', 'upload');
Route::post('/file-upload', 'GeneralController@store');
Route::get('/view-uploads', 'GeneralController@viewUploads');

Views

It’s time to create views. If you want to follow my directory structure then create a “components” folder inside “views” directory. This really makes it easier for eyes by taking out all the components out of your sight.

Directory Structure

I have multiple components in there but here we will only see relevant ones. The rest can be seen in GitHub Repository.

Your “upload_form.blade.php” should contain the following code.

<div class="container-fluid">
<div class="row justify-content-center">
<h2 class="card-header w-100 m-1 text-center">Upload Image</h2>
</div>
<div class="row justify-content-center">
{{-- enctype attribute is important if your form contains file upload --}}
{{-- Please check https://www.w3schools.com/tags/att_form_enctype.asp for further info --}}
<form class="m-2" method="post" action="/file-upload" enctype="multipart/form-data">
@csrf
<div class="form-group">
{{-- <label for="name">File Name</label>--}}
<input type="text" class="form-control" id="name" placeholder="Enter file Name" name="name">
</div>
<div class="form-group">
<label for="image">Choose Image</label>
<input id="image" type="file" name="image">
</div>
<button type="submit" class="btn btn-dark d-block w-75 mx-auto">Upload</button>
</form>
</div>
@include('components.errors')
</div>

Now, create a view for your Upload page and name it “upload.blade.php”. Component approach makes your views cleaner and small, plus you can re-use them.

@extends('components.master')
@include('components.nav')
@include('components.upload_form')

@component('components.success')

@endcomponent

Create another view “view_uploads.blade.php” inside views directory to show the uploaded images.

@extends('components.master')
@include('components.nav')

<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th class="cs-p-1">Name</th>
<th class="cs-p-1">URL</th>
</tr>
</thead>

@forelse($images as $image)
<tr>
<td class="cs-p-1">{{ $image->name }}</td>
<td class="cs-p-1"><a href="{{ $image->url }}">View Image</a></td>
</tr>
@empty
<p>No Images at the moment</p>
@endforelse
</table>
</div>

Use of @forelse allows you to display a message using @empty when the array is empty.

Finally replace all the code inside “welcome.blade.php”

@extends('components.master')
@include('components.nav')

Controller

Finally here is the most awaited part. Create a controller “GeneralController” and replace any code with following code.

<?php

namespace App\Http\Controllers;

use App\Models\File;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Session;

class GeneralController extends Controller
{
public function store (Request $request) {

if ($request->hasFile('image')) {
// Let's do everything here
if ($request->file('image')->isValid()) {
//
$validated = $request->validate([
'name' => 'string|max:40',
'image' => 'mimes:jpeg,png|max:1014',
]);
$extension = $request->image->extension();
$request->image->storeAs('/public', $validated['name'].".".$extension);
$url = Storage::url($validated['name'].".".$extension);
$file = File::create([
'name' => $validated['name'],
'url' => $url,
]);
Session::flash('success', "Success!");
return \Redirect::back();
}
}
abort(500, 'Could not upload image :(');
}

public function viewUploads () {
$images = File::all();
return view('view_uploads')->with('images', $images);
}
}

Let’s walk through the code step by step.

$request->hasFile('image')

checks if the request actually has the image. this function returns true request has the file. If you do not check this then you would run into something like

application blows up because image is not present in request and we didn’t check for it.
$request->file('image')->isValid()

checks if the upload was valid and there is no data corruption.

$validated = $request->validate([
'name' => 'string|max:40',
'image' => 'mimes:jpeg,png|max:1024',
]);

Next, you have to validate the request. Here we are specifying that image name should be a string with max size of 40 characters AND image must either be “jpg” OR “png” AND maximum size of image is 1MB. Size is provided in KBs so 1024 == 1MB.

$extension = $request->image->extension();

Here, we get the file extension.

$request->image->storeAs('/public', $validated['name'].".".$extension);

Since I want to store the file in a directory of my choice with a custom name so I am using storeAs method. Keep in mind that extension also goes as part of file name. The user provided us only file name but didn’t provide the extension.

$validated['name'].".".$extension

This is just concatenation of “.” with file name and then concatenation of extension with file name.

You can use extension method on file to get the file extension.

$url = Storage::url($validated['name'].".".$extension);

Here, we generate the url to store in DB. this url will be provided to users so they can view the file.

url() takes the file name and creates a url for it.

$file = File::create([
'name' => $validated['name'],
'url' => $url,
]);

Next, we simply create a record in DB.

Session::flash('success', "Success!");
return \Redirect::back();

Last step, here we flash a success message to the session and redirect back.

abort(500, 'Could not upload image :(');

Since all our code is inside if blocks so if none of them executes then an error will be generated and shown to the user. By default, Laravel shows the default error message for all errors. It doesn’t the show the custom message the developer sets.

I have added the code in repository to display developer set error message. You can also read my article about fixing those error pages so they can display the message you set!

--

--

MacTavish

Contributor in Tor Project, Web developer, Gamer. I tend to write article to help beginners.