How to Upload and Store Files with Laravel 8

Kenpachi Zaraki
6 min readApr 27, 2021

If you need to create a form that requires users to provide a picture or some other media, this article will prove useful to you. We will be taking a look at how you create a form with a drop zone that allows users to drag-and-drop files they wish to upload as well as what you will need on your Laravel backend to store these files. This tutorial assumes you know your way around the file system of a Laravel project. Let’s do this!!!

Case Scenario

Let’s assume we need to store the name, gender and picture for each student in the school. We therefore create a form where students can provide these details. In our MySQL database, we have a table called students that has columns called name, gender and file_path to hold the name, gender and the path to the picture of each student respectively (The table also has default columns provided by Laravel such as created_at and updated_at).

1. Create a new laravel project

We will name our laravel project students. To create it we will open our command line, navigate to the folder we wish to store our project in and run the following line:

composer create-project laravel/laravel students

Run the following line to navigate into the folder:

cd students

Don’t forget to update your .env file with the appropriate database name.

2. Create a “Student” model and its associated controller and migration file

You can do this by running the following line of code on your command line:

php artisan make:model Student -cm

3. Create a view where you can show your form

Navigate to your views folder and create a file named students.blade.php.

4. Add jQuery and Dropzone CDNs to your view file

The Dropzone CDNs will allow users to add files to the form via drag-and-drop. jQuery is included to help with form submission. Files dragged into dropzone widgets are submitted seperately from the rest of the form’s elements. Here are the CDNs you will need:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/basic.css" integrity="sha512-+Vla3mZvC+lQdBu1SKhXLCbzoNCl0hQ8GtCK8+4gOJS/PN9TTn0AO6SxlpX8p+5Zoumf1vXFyMlhpQtVD5+eSw==" crossorigin="anonymous" /><script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/min/dropzone.min.js" integrity="sha512-VQQXLthlZQO00P+uEu4mJ4G4OAgqTtKG1hri56kQY1DtdLeIqhKUp9W/lllDDu3uN3SnUNawpW7lBda8+dSi7w==" crossorigin="anonymous"></script><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>

After adding the CDNs to your HTML layout in students.blade.php, your view file should look like this:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/basic.css" integrity="sha512-+Vla3mZvC+lQdBu1SKhXLCbzoNCl0hQ8GtCK8+4gOJS/PN9TTn0AO6SxlpX8p+5Zoumf1vXFyMlhpQtVD5+eSw==" crossorigin="anonymous" /><title>Document</title></head><body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/min/dropzone.min.js" integrity="sha512-VQQXLthlZQO00P+uEu4mJ4G4OAgqTtKG1hri56kQY1DtdLeIqhKUp9W/lllDDu3uN3SnUNawpW7lBda8+dSi7w==" crossorigin="anonymous"></script><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script></body></html>

4. Edit your migration file

The file_path column is made nullable because its value is not provided along with the values for the other columns.

<?phpuse Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;class CreateStudentsTable extends Migration{/*** Run the migrations.** @return void*/public function up(){Schema::create('students', function (Blueprint $table) {    $table->id();    $table->string('name');    $table->string('gender');    $table->string('file_path')->nullable();    $table->timestamps();  });}/*** Reverse the migrations.** @return void*/public function down(){  Schema::dropIfExists('students');}}

5. Run your migrations on the command line

Run the following command on your command line to create your tables in your database:

php artisan migrate

6. Create your form

Your form should something like this:

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <meta http-equiv="X-UA-Compatible" content="ie=edge">  <meta name="csrf-token" content="{{ csrf_token() }}">  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/basic.css" integrity="sha512-+Vla3mZvC+lQdBu1SKhXLCbzoNCl0hQ8GtCK8+4gOJS/PN9TTn0AO6SxlpX8p+5Zoumf1vXFyMlhpQtVD5+eSw==" crossorigin="anonymous" />  <title>Add Student</title>  <style>    .dropzoneDragArea {      background-color: #fbfdff;      border: 1px dashed #c0ccda;      border-radius: 6px;      padding: 60px;      text-align: center;      margin-bottom: 15px;      cursor: pointer;    }    .dropzone{      box-shadow: 0px 2px 20px 0px #f2f2f2;      border-radius: 10px;    }</style></head><body>  <h1>Provide Your Details here</h1>  <form method="post" name="demoform" id="demoform" action="{{ route('student.add') }}" enctype="multipart/form-data">  @csrf    <input type="hidden" class="student_id" name="student_id" id="student_id" value=""><div><label>Name:</label><input type="text" name="name"></div><div><label>Gender:</label><label><input type="radio" name="gender" value="male"> Male </label><label><input type="radio" name="gender" value="female"> Female </label></div><br><div><div id="dropzoneDragArea" class="dz-default dz-message dropzoneDragArea"><span>Upload file</span></div><div class="dropzone-previews"></div></div><div class="form-group"><button type="submit" class="btn btn-md btn-primary">create</button></div></form><script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/min/dropzone.min.js" integrity="sha512-VQQXLthlZQO00P+uEu4mJ4G4OAgqTtKG1hri56kQY1DtdLeIqhKUp9W/lllDDu3uN3SnUNawpW7lBda8+dSi7w==" crossorigin="anonymous"></script><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script>Dropzone.autoDiscover = false;// Dropzone.options.demoform = false;let token = $('meta[name="csrf-token"]').attr('content');$(function() {var myDropzone = new Dropzone("div#dropzoneDragArea", {paramName: "file",url: "{{ route('student.store_file') }}",previewsContainer: 'div.dropzone-previews',addRemoveLinks: true,autoProcessQueue: false,uploadMultiple: false,parallelUploads: 1,maxFiles: 1,params: {_token: token},// The setting up of the dropzoneinit: function() {var myDropzone = this;//form submission code goes here$("form[name='demoform']").submit(function(event) {//Make sure that the form isn't actully being sent.event.preventDefault();URL = $("#demoform").attr('action');formData = $('#demoform').serialize();$.ajax({type: 'POST',url: URL,data: formData,success: function(result){if(result.status == "success"){// fetch the useidvar student_id = result.student_id;$("#student_id").val(student_id); // inseting student_id into hidden input field//process the queuemyDropzone.processQueue();}else{console.log("error");}}});});//Gets triggered when we submit the image.this.on('sending', function(file, xhr, formData){//fetch the student id from hidden input field and send that studentid with our imagelet student_id = document.getElementById('student_id').value;formData.append('student_id', student_id);});this.on("success", function (file, response) {  alert("Student added successfully!!");  location.reload();});this.on("queuecomplete", function () {});}});});</script></body></html>

jQuery is used to submit the form data without the image after which it receives a response containing the student_id of the student whose information was just stored. It then submits the image from the drop zone along with the id received.

Make sure the meta csrf tag is added to your view or you might face problem.

7. Update your routes

In your routes/web.php file, add the following lines

//Route for showing the formRoute::get('add-student', [StudentController::class, 'create']);//Route for storing all form data except the fileRoute::post('store-student', [StudentController::class, 'store'])->name('student.add');//Route for storing the fileRoute::post('store-file', [StudentController::class, 'storeFile'])->name('student.store_file');

As explained in step 4, we will need to upload our picture seperately from the rest of the form data. Two different routes are therefore provided accordingly.

To access the form on your browser:

  1. Start the server from your command line with the following command:
php artisan serve

2. Type the following into the address bar of your browser and press enter

http://localhost:8000/add-student

8. Update your controller

You will need to add three methods to your controller: the first will show the view where student data is entered, the second will store form data (without the image) and the third will store the student image. Your StudentController.php file should look like this.

<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;use App\Models\Student;class StudentController extends Controller{  public function create(){     return view('students');  }  // This method stores the form data (name and gender)  public function store(Request $request){    try {      $student = new Student();      $student->name =  $request->name;      $student->gender =  $request->gender;      $student->save();    }    catch (\Exception $e) {      return response()->json(['status'=>'exception', 'msg'=>$e->getMessage()]);    }    //The id of the student is returned in the response so it can be used to update the student's record with the file's path later on    return response()->json(['status'=>"success", 'student_id'=>$student->id]);  }
//This method stores the form image using the id returned to the view by the store() method public function storeFile(Request $request){ if($request->file('file')){ $student = new Student(); $student_id = $request->student_id; //we are storing request file in the folder named 'files'
$path = $request->file('file')->store('files');
// we are updating our file_path column by using student_id to find the right record $student->where('id', $student_id)->update(['file_path'=>$path]); return response()->json(['status'=>"success"]); } }}

9. Test it!! Fill the form and submit it

Now, if you go to storage/app folder, you will find a new folder named files and the uploaded image will be available in this folder.

If you want to give a name to the file you are uploading, you can use storeAs() method instead of the store() method used in the controller.

The file path that is saved to your database will look something like this

files/A0wjnHie4xlNR1o6qxHihU259yblArpIToGIEps8.png

Happy Coding !!! All the best !!!

Related Articles

Laravel 8: How to Display and Download Files

Laravel 8: MySQL Database Query Tip Sheet #1

Laravel 8: MySQL Database Query Tip Sheet #2

Laravel 8: MySQL Database Query Tip Sheet #3

Laravel 8: MySQL Database Query Tip Sheet #4

--

--