How to upload a file using AJAX in Laravel in the best way?

Laravel allows us to upload files in many ways. We have learnt of the use of simple Laravel move methods to upload single and multiple files. However, AJAX has the advantage that it allows you to upload a file to the server without reloading the page. Let us now learn how to upload a file using AJAX and display preview in Laravel 8.

Step 1: Create Install Laravel and Create new Project

Firstly, we create a new Laravel project.

composer create-project laravel/laravel upload_file_ajax

Step 2: Create new Routes

Secondly, we create new routes for the functions we will define in the Controller.

Route::get('/', [UploadController::class, 'index']);
Route::post('/uploadFile', [UploadController::class, 'uploadFile'])->name('uploadFile');

Step 3: Create new Controller Class

Next, we add the controller class. This handles the file inputs to and from the user blade.

<?php

class UploadController extends Controller {

   public function index(){
      return view('upload_view');
   }

   public function uploadFile(Request $request){

      $data = array();

      $validator = Validator::make($request->all(), [
         'file' => 'required|mimes:png,jpg,jpeg,csv,txt,pdf|max:2048'
      ]);

      if ($validator->fails()) {

         $data['success'] = 0;
         $data['error'] = $validator->errors()->first('file');// Error response

      }else{
         if($request->file('file')) {

             $file = $request->file('file');
             $filename = time().'_'.$file->getClientOriginalName();

             // File extension
             $extension = $file->getClientOriginalExtension();

             // File upload location
             $location = 'files';

             // Upload file
             $file->move($location,$filename);
             
             // File path
             $filepath = url('files/'.$filename);

             // Response
             $data['success'] = 1;
             $data['message'] = 'Uploaded Successfully!';
             $data['filepath'] = $filepath;
             $data['extension'] = $extension;
         }else{
             // Response
             $data['success'] = 2;
             $data['message'] = 'File not uploaded.'; 
         }
      }

      return response()->json($data);
   }

}

The Controller Class has two methods.

  • index() – Loads the blade view where user uploads file.
  • uploadFile() – The method is used to upload the file.

A $data Array stores the return response.

We define file validation by setting the max file size to 2 MB (2048 Kb).

If the file validation fails, then we assign 0 to $data['success'] and validation response to $data['error'].

If the file validation passes, then we assign file name to $filename and file extension to $extension variable. Assign upload location "files" to $location variable.

We will execute $file->move($location,$filename); to store the file and the location mentioned behind the $location variable.

We will assign 1 to $data['success']'Uploaded Successfully!' to $data['message'], file path to $data['filepath'], and file extension to $data['extension'].

If the file is not uploaded then we assign 2 to $data['success'] and it displays the ‘File not uploaded.' message to $data['message'].

The response returns $data Array in JSON format.

Step 4: Create new View

Now we will create the upload_view.blade.php file in resources/views/ directory. Success message for file upload as a response will come in <div id="responseMsg"> using jQuery.

<!DOCTYPE html>
<html>
<head>
<title>How to upload a file using AJAX in Laravel 8 - scratchcoding.dev</title>

<!-- Meta -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="csrf-token" content="{{ csrf_token() }}">

<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">

<style type="text/css">
.displaynone{
display: none;
}
</style>
</head>
<body>

  <div class="container">

    <div class="row">

      <div class="col-md-12 col-sm-12 col-xs-12">

        <!-- Response message -->
        <div class="alert displaynone" id="responseMsg"></div>

        <!-- File preview --> 
        <div id="filepreview" class="displaynone" > 
          <img src="" class="displaynone" with="200px" height="200px"><br>

          <a href="#" class="displaynone" >Click Here..</a>
        </div>

        <!-- Form -->
        <div class="form-group">
           <label class="control-label col-md-3 col-sm-3 col-xs-12" for="name">File    <span class="required">*</span></label>
           <div class="col-md-6 col-sm-6 col-xs-12">

              <input type='file' id="file" name='file' class="form-control">

              <!-- Error -->
              <div class='alert alert-danger mt-2 d-none text-danger' id="err_file"></div>

           </div>
        </div>

        <div class="form-group">
           <div class="col-md-6">
              <input type="button" id="submit" value='Submit' class='btn btn-success'>
           </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Script -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script type="text/javascript">
  var CSRF_TOKEN = document.querySelector('meta[name="csrf-token"]').getAttribute("content");

  $(document).ready(function(){

    $('#submit').click(function(){
   
      // Get the selected file
      var files = $('#file')[0].files;

      if(files.length > 0){
         var fd = new FormData();

         // Append data 
         fd.append('file',files[0]);
         fd.append('_token',CSRF_TOKEN);

         // Hide alert 
         $('#responseMsg').hide();

         // AJAX request 
         $.ajax({
           url: "{{route('uploadFile')}}",
           method: 'post',
           data: fd,
           contentType: false,
           processData: false,
           dataType: 'json',
           success: function(response){

             // Hide error container
             $('#err_file').removeClass('d-block');
             $('#err_file').addClass('d-none');

             if(response.success == 1){ // Uploaded successfully

               // Response message
               $('#responseMsg').removeClass("alert-danger");
               $('#responseMsg').addClass("alert-success");
               $('#responseMsg').html(response.message);
               $('#responseMsg').show();

               // File preview
               $('#filepreview').show();
               $('#filepreview img,#filepreview a').hide();
               if(response.extension == 'jpg' || response.extension == 'jpeg' || response.extension == 'png'){

                  $('#filepreview img').attr('src',response.filepath);
                  $('#filepreview img').show();
               }else{
                  $('#filepreview a').attr('href',response.filepath).show();
                  $('#filepreview a').show();
               }
             }else if(response.success == 2){ // File not uploaded

               // Response message
               $('#responseMsg').removeClass("alert-success");
               $('#responseMsg').addClass("alert-danger");
               $('#responseMsg').html(response.message);
               $('#responseMsg').show();
             }else{
               // Display Error
               $('#err_file').text(response.error);
               $('#err_file').removeClass('d-none');
               $('#err_file').addClass('d-block');
             } 
           },
           error: function(response){
              console.log("error : " + JSON.stringify(response) );
           }
         });
      }else{
         alert("Please choose a file.");
      }

    });
  });
  </script>

</body>
</html>

The <img > and <a > element in <div id="filepreview"> are to display a file preview according to the file extension using jQuery.

We will create a file element and a button. Errors will appear in <div id="err_file"> if the file is not validated using jQuery.

On clicking the button (submit), we will read the selected file and assign to files variable.

If the file is not selected then an alert("Please select a file."); will pop up. Otherwise, we will pass the selected file using a FormData object.

We can send the AJAX POST request to "{{route('uploadFile')}} where we pass FormData Object as data.

On successful callback, we will check upload status.

If response.success == 1, it means the file is successfully uploaded. We can then display the response message and preview the file according to the file extension.

If response.success == 2, it means the file is not uploaded. Display the appropriate response message.

If response.success does not equal 1 or 2, it means the file is not validated. So, display the corresponding error message.

Conclusion

Use the FormData object allows us to pass extra data while sending AJAX request e.g. fd.append('filename',"file 1");. Here, fd is FormData object.

We need to assure to check upload_max_filesize and post_max_size values in the php.ini file to allow large files to upload without errors.