How to Display Existing Files in Dropzone with CakePHP 4

Dropzone is a well-known JavaScript library that enables to create an easy-to-use drag-and-drop file upload interface for web applications. It’s a powerful tool that makes managing file uploads easier for both developers and end users.

Dropzone container gets empty when the page gets reloaded, for displaying previously uploaded file you have to write some extra code.

In this tutorial, I will discuss how to display existing files in the Dropzone container in CakePHP 4 project. I will walk you through the process of creating a Dropzone element that will display all the files that have been uploaded previously.

How to Display Existing Files in Dropzone with CakePHP 4


Contents

  1. Include jQuery, Dropzone and CSRF token
  2. Create Controller
  3. Create Template
  4. Output
  5. Conclusion

1. Include jQuery, Dropzone and CSRF token

I am including the Dropzone and jQuery library and CSRF token in templates/layout/default.php file.

Included jQuery only to send AJAX request to fetch existing files.

<?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>

You can download Dropzone library from their official website or you can use CDN –

<!-- Dropzone -->
<?= $this->Html->css(['https://unpkg.com/dropzone@5/dist/min/dropzone.min.css']) ?>
<?= $this->Html->script('https://unpkg.com/dropzone@5/dist/min/dropzone.min.js'); ?>

Completed Code

<?php
$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html>
     <head>
          <?= $this->Html->charset() ?>
          <meta name="viewport" content="width=device-width, initial-scale=1">

          <!-- CSRF Token -->
          <?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>

          <title> 
               <?= $cakeDescription ?>:
               <?= $this->fetch('title') ?>
          </title>
          <?= $this->Html->meta('icon') ?>

          <link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">

          <?= $this->Html->css(['normalize.min', 'milligram.min', 'cake']) ?>

          <!-- jQuery -->
          <?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>

          <!-- Dropzone -->
          <?= $this->Html->css(['https://unpkg.com/dropzone@5/dist/min/dropzone.min.css']) ?>
          <?= $this->Html->script('https://unpkg.com/dropzone@5/dist/min/dropzone.min.js'); ?>

          <?= $this->fetch('meta') ?>
          <?= $this->fetch('css') ?>
          <?= $this->fetch('script') ?>
     </head>
     <body>
          <nav class="top-nav">
               <div class="top-nav-title">
                    <a href="<?= $this->Url->build('/') ?>"><span>Cake</span>PHP</a>
               </div>
               <div class="top-nav-links">
                    <a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
                    <a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
               </div>
          </nav>
          <main class="main">
               <div class="container">
                    <?= $this->Flash->render() ?>
                    <?= $this->fetch('content') ?>
               </div>
          </main>
          <footer>
          </footer>
     </body>
</html>

2. Create Controller

  • Create a FileuploadController.php file in src/Controller/ folder.
  • Create FileuploadController Class that extends AppController.
  • Include Cake\Filesystem\Folder to create folder, Cake\Filesystem\File to create and read file details, and  Cake\Validation\Validator to add validation rules.

Create 3 methods –

  • index()
  • readExistingFiles() – This method is use to return existing files for Dropzone.

To read all files from the uploads folder, create a Folder Class object. Using read() method load all folder and files names in ascending order, for example, purpose I am skipping index.php file from the uploads folder if it exists. You can specify the filename with an extension in [] that you want to skip.

$files[0] - Stores folders names.
$files[1] - Stores all files names.

To read files, loop on $files[1]. To obtain file details, create an object of the File Class. Get the file size and extension using the size() and ext() methods, respectively. Check file extension is of image type or not. If it is image type then assign true to $isImage.

Store file name, size, path, and isImage in $files_arr Array. Call the close() method to close the currently open file.

Return $files_arr Array in JSON format.

  • upload() – This method is use to upload Dropzone file.

Define file validation, if file is not validated then store error message to $response['message'] and 0 to $response['success']. Return $response Array in JSON format.

If file is validated then upload the file to uploads folder, store success message to $response['message'] and 1 to $response['success']. Return $response Array in JSON format.

Completed Code

<?php
declare(strict_types=1);

namespace App\Controller;
use Cake\Filesystem\Folder;
use Cake\Filesystem\File;
use Cake\Validation\Validator;

class FileuploadController extends AppController
{

     public function index(){

     }

     // Read Existing files for Dropzone
     public function readExistingFiles(){

          ## Reading files from 'uploads' folder
          $dir = new Folder(WWW_ROOT . 'uploads' . '/');

          ## Reading file in ascending order and skip index.php file if exists
          $files = $dir->read(true,['index.php']);

          ## Loop on files[1]
          $files_arr = array();
          foreach ($files[1] as $file) {

                ## File name with extension
                $filename = $file;

                ## File absolute path
                $filepath = WWW_ROOT . 'uploads' . DS.$file;

                ## Create File object
                $fileObj = new File($filepath, true, 0644);

                ## File size
                $fileSize = $fileObj->size();

                ## File extension
                $extension = strtolower($fileObj->ext());

                ## Check file is image type or not
                $imageext_arr = array("jpg","jpeg","png");
                $isImage = false;
                if(in_array($extension,$imageext_arr)){
                      $isImage = true;
                }

                ## File path
                $path = DS.'uploads'.DS.$filename;

                ## Store file details in Array
                $files_arr[] = array(
                      'name' => $filename,
                      'size' => $fileSize,
                      'path' => $path,
                      'isImage' => $isImage,
                );

                ## Close File
                $fileObj->close();
          }

          echo json_encode($files_arr);
          die;

     }

     // Upload Dropzone file
     public function upload(){

          ## Validate File
          $validator = new Validator();

          $validator
              ->notEmptyFile('file')
              ->add('file', [
                    'mimeType' => [
                          'rule' => ['mimeType',['image/jpg','image/png','image/jpeg','application/pdf']],
                          'message' => 'File type must be .jpg,.jpeg,.png,.pdf',
                    ],
                    'fileSize' => [
                          'rule' => ['fileSize','<', '2MB'],
                          'message' => 'File size must be less than 2MB',
                    ]
              ]);
          $errors = $validator->validate($this->request->getData());

          $response = array();
          if (!empty($errors)) { // Not validated

                ## Return response with error message
                $fileerrors = $errors['file'];
                $errormsg = "";
                foreach($fileerrors as $error){
                     $errormsg = $error;
                }
                $response['message'] = $errormsg;
                $response['success'] = 0;
                echo json_encode($response);
                die;

          }

          ## Upload file
          $fileEl = $this->request->getData('file');

          // File details
          $filename = $fileEl->getClientFilename();
          $type = $fileEl->getClientMediaType();
          $size = $fileEl->getSize();
          $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
          $tmpName = $fileEl->getStream()->getMetadata('uri');
          $error = $fileEl->getError();

          if($error == 0){

                ## Upload location
                $location = WWW_ROOT . 'uploads' . DS;// webroot/uploads/

                ## Check upload location exists or not
                $folder = new Folder();
                $checkfolder = $folder->inPath($location, true);
                if(!$checkfolder){ // Not exists
                      if (!$folder->create($location)) {

                            $response['message'] = 'Folder is not created.';
                            $response['success'] = 0;

                            echo json_encode($response);
                            die;

                      }
                }

                ## Upload file
                $targetPath = $location.$filename;
                $fileEl->moveTo($targetPath);

                ## Uploaded file path
                $filepath = "/uploads/".$filename;

                $response['message'] = 'File uploaded successfully.';
                $response['success'] = 1;

                echo json_encode($response);
                die;

          }else{

                $response['message'] = 'File is not uploaded.';
                $response['success'] = 0;

                echo json_encode($response);
                die;
          }

     }
}

3. Create Template

Create Fileupload folder in templates/ location. In the Fileupload folder create index.php file – templates/Fileupload/index.php.

Create a <form >

<form action="<?= $this->Url->build(['controller' => 'fileupload','action' => 'upload']) ?>" class='dropzone' ></form>

Script

  • Read CSRF token from <meta name="csrfToken" > tag and assign it to csrfToken variable.
  • Disable Dropzone auto initialization by setting Dropzone.autoDiscover to false.
  • Initialize Dropzone on dropzone class and set file restrictions.
  • Restrict file upload using maxFilesize and acceptedFiles option.
  • Pass CSRF token using header – "X-CSRF-Token" : csrfToken .
  • Using success event handle Dropzone response. If success == 0 means file is not uploaded and alert the error message.

Display existing files – 

  • Using init option to load existing files.
  • Send AJAX request to <?= $this->Url->build(['controller' => 'fileupload','action' => 'readExistingFiles']) ?> and set dataType: 'json'.
  • On AJAX successful callback loop on the response using $.each().
  • Create an object mockFile that has name and size values – { name: value.name, size: value.size }.
  • Call emit() method on Dropzone instance to add file in Dropzone container.
  • Not emit() thumbnail if file is not image type.
  • After adding the file and its thumbnail call emit() complete.
Completed Code
<style type="text/css">
.dz-preview .dz-image img{
      width: 100% !important;
      height: 100% !important;
      object-fit: cover;
}
</style>

<div class="row">
     <div style="width: 100%;">

          <!-- Dropzone -->
          <form action="<?= $this->Url->build(['controller' => 'fileupload','action' => 'upload']) ?>" class='dropzone' ></form>

     </div>
</div>

<!-- Script -->
<script type="text/javascript">

// Read CSRF Token
var csrfToken = document.querySelector('meta[name="csrfToken"]').content;

Dropzone.autoDiscover = false;
var myDropzone = new Dropzone(".dropzone",{
      maxFilesize: 2, // 2 mb
      acceptedFiles: ".jpeg,.jpg,.png,.pdf",
      headers: {
           "X-CSRF-Token" : csrfToken
      },
      init: function() {
           myDropzone = this;

           // Fetch existing files
           $.ajax({
                 url: '<?= $this->Url->build(['controller' => 'fileupload','action' => 'readExistingFiles']) ?>',
                 type: 'get',
                 dataType: 'json',
                 success: function(response){

                       // Add File to Dropzone container
                       $.each(response, function(key,value) {
                            var mockFile = { name: value.name, size: value.size };

                            myDropzone.emit("addedfile", mockFile);
                            if(value.isImage){
                                  myDropzone.emit("thumbnail", mockFile, value.path);
                            }

                            myDropzone.emit("complete", mockFile);

                       });

                 }
           });
      }
});

myDropzone.on("success", function(file, response) {
      response = JSON.parse(response);

      if(response.success == 0){ // Error
           alert(response.message);
      }

});
</script>

4. Output

View Output


5. Conclusion

By combining CakePHP and Dropzone, you can provide an excellent user experience when it comes to file uploads in your web applications.

To display existing files in Dropzone you have to send AJAX a request to load files and by using emit() method add files to Dropzone container.

You can view more CakePHP 4 tutorials on this website. I hope this tutorial helps you to successfully display existing files in Dropzone.

If you found this tutorial helpful then don't forget to share.

Leave a Comment