Search code examples
javascriptphpajaximageupload

Naming files on server using Ajax's drag and drop


I'm using a basic multiple upload Javascript that doesn't allow to re-order files. I would like to be able to drag and drop pictures and save their new position on the server. The tricky part is that I do not store those files in a database.

The upload process works that way : as you can see, it's a simple loop that rename the pics from 001.jpg to 099.jpg and place them in the right folder.

if(isset($_FILES['images']) && $_FILES['images']['size'] > 0) {
        if(!file_exists('images/upload/' . $id_client . '/photos/' . $gallery)) {
            mkdir('images/upload/' . $id_client . '/photos/' . $gallery);
        }
        else {
            $gallery = $data_m[0]['gallery'];
        }
        $i = htmlspecialchars(trim($_POST['nb_img']) + 1);
        foreach($_FILES['images']['tmp_name'] as $index => $tmpName) {
            if(!empty($tmpName) && is_uploaded_file($tmpName)) {
                if(substr($_FILES['images']['type'][$index], 0, 5) == "image") {
                    if($i < 10) {
                        $img_url = '00' . $i . '.' . pathinfo($_FILES['images']['name'][$index], PATHINFO_EXTENSION);
                    }
                    else {
                        $img_url = '0' . $i . '.' . pathinfo($_FILES['images']['name'][$index], PATHINFO_EXTENSION);    
                    }
                    move_uploaded_file( $tmpName, $dir_upload . '/photos/' . $gallery . '/'. $img_url);
                }
            }
            $i++;
        }
    }

The display is as simple : the page just checks the folder and shows the content in alphabetical order :

if(!empty($data['gallery']) && file_exists($dir_upload . '/photos/' . $data['gallery'])) {
            $dirname = $dir_upload . '/photos/' . $data['gallery'];
            $gallery = scandir($dirname);
            asort($gallery);
            $row = 1;
            echo '<div class="gallery">';
                echo '<div class="row">';
                foreach($gallery as $pix) {
                    if(!empty($pix) && $pix != '.' && $pix != '..') {
                        if($row >5) { 
                            echo '</div>'; 
                            echo '<div class="row">';
                            $row = 1; 
                        }
                        $url_img = $dirname . '/' . $pix;
                        echo '<a class="col-md mt-1 mr-1 mb-1 ml-1 btn btn-secondary img-'.$row.' disabled" href="' . $url_img . '" rel="clearbox[gallery=' . $data['gallery'] . ']" style="background:url(' . $url_img . ') center; background-size:cover; height:210px;">';
                        echo '</a>';
                        $row++;
                    }
                }
                echo '</div>';
            echo '</div>';
        }

My immediate need would be to be able to simply drag and drop the pics and rename them on the server. For example, if I drag 005.jpg in third position, it would rename it 002-1608378266.jpg (adding the actual Timestamp so it places itself before 003.jpg without erasing the previous file).

I'm aware it's not the best option (not even sure that's doable) and I'm already working on a database solution, but I need an emergency solution to calm down users while I develop the new script.

Thanks to those who will try and help me !


Solution

  • We were able to get to the bottom of things in the chat, and here is a proposed solution:

    The order of images is stored in a JSON file. That file is then read when we want to get the list of images a gallery contains, and updated both when new images are added, and when the user sorts these images.

    In order to accomplish this, I made a file containing only utility functions, and a demo page:

    utils.php

    <?php 
    
    /**
     * Returns the path to a gallery's directory
     */
    function getClientGalleryDirPath($clientId, $gallery) {
      return 'images/upload/' . $clientId . '/photos/' . $gallery;
    }
    
    /**
     * Returns the path to a gallery's data file
     */
    function getClientGalleryDataPath($clientId, $gallery) {
      return getClientGalleryDirPath($clientId, $gallery) . '/data.json';
    }
    
    /**
     * Makes sure the client's gallery has a directory, and a JSON file
     * containing the list of images
     */
    function ensureClientGalleryExists($clientId, $gallery) {
      $dirPath = getClientGalleryDirPath($clientId, $gallery);
    
      if(!file_exists($dirPath)) {
        mkdir($dirPath, 0755, true);
      }
    
      $jsonPath = getClientGalleryDataPath($clientId, $gallery);
    
      if (!file_exists($jsonPath)) {
        // Create an empty Array
        $data = [];
    
        // If pictures exist, we'll add them
        $pictureFiles = scandir($dirPath);
        foreach($pictureFiles as $pic) {
          if(!empty($pic) && $pic != '.' && $pic != '..') {
              array_push($data, $pic);
          }
        }
        // Make the Array a JSON string, and save it
        $json = json_encode($data);
        file_put_contents($jsonPath, $json);
      }
    }
    
    /**
     * Ensures the gallery is created, and returns the data
     */
    function getOrCreateClientGallery($clientId, $gallery) {
      // Make sure it exists
      ensureClientGalleryExists($clientId, $gallery);
    
      $dataPath = getClientGalleryDataPath($clientId, $gallery);
      $json = file_get_contents($dataPath);
    
      return json_decode($json, true);
    }
    
    /**
     * Saves new data in a client's gallery
     */
    function saveClientGallery($clientId, $gallery, $data) {
      // Make sure it exists
      ensureClientGalleryExists($clientId, $gallery);
    
      $dataPath = getClientGalleryDataPath($clientId, $gallery);
      $json = json_encode($data);
    
      return file_put_contents($dataPath, $json);
    }
    
    /**
     * Uploads pictures, and updates the gallery data
     */
    function addPicturesToGallery($clientId, $gallery, $files) {
      // Get the existing data
      $data = getOrCreateClientGallery($clientId, $gallery);
      $dirPath = getClientGalleryDirPath($clientId, $gallery);
    
      foreach($files['tmp_name'] as $index => $tmpName) {
        if (
             !empty($tmpName) &&
             is_uploaded_file($tmpName) &&
             substr($files['type'][$index], 0, 5) == "image"
           ) {
             // Create a unique name, and move the file
             $newName = uniqid() . '.' . pathinfo($files['name'][$index], PATHINFO_EXTENSION);
             move_uploaded_file($tmpName, $dirPath . '/'. $newName);
             // Add the new name to $data 
             array_push($data, $newName);
        }
      }
    
      // We're done, save the data
      saveClientGallery($clientId, $gallery, $data);
    }
    
    /**
     * Compares the new list with the previous one, and updates it
     */
    function  reorderClientGallery($clientId, $gallery, $newData) {
      $oldData = getOrCreateClientGallery($clientId, $gallery);
    
      // Make sure that every element is present in both Arrays
      // (otherwise there's something wrong with the provided data)
      if (
           count(array_diff($oldData, $newData)) == 0 &&
           count(array_diff($newData, $oldData)) == 0
         ) {
           saveClientGallery($clientId, $gallery, $newData);
      }
    }
    

    index.php

    <?php 
    
    require_once('utils.php');
    
    // Just for the demo, let's say that:
    $clientId = 'robert';
    $gallery = 'my-first-gallery';
    
    // If pictures were sent, upload them
    if (isset($_FILES['images']) && $_FILES['images']['size'] > 0) {
      addPicturesToGallery($clientId, $gallery, $_FILES['images']);
    }
    
    // If a picture ordering was sent, apply it
    if (isset($_POST['order'])) {
      reorderClientGallery($clientId, $gallery, json_decode($_POST['order']));
    }
    
    
    
    // To avoid cluttering the HTML below, I declared this here
    function displayGalleryItems($clientId, $gallery) {
      $dirPath = getClientGalleryDirPath($clientId, $gallery);
      // Get the gallery and display it
      $data = getOrCreateClientGallery($clientId, $gallery);
      foreach($data as $pic) {
        echo '<div class="pic" data-pic="' . $pic . '" style="background-image: url(\'' . $dirPath . '/' . $pic . '\')"></div>';
      }
    }
    
    ?><!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>My gallery</title>
      <style>
        #saveBtn { display: none; } /* will only be displayed after reordering */
        .pic { display: inline-block; width: 250px; height: 190px; background-size: cover; margin: .5em; }
      </style>
    </head>
    <body>
      <h1><?=$gallery?></h1>
    
      <p>Add pictures to your gallery:</p>
      <form method="post" enctype="multipart/form-data">
        <input type="file" name="images[]" multiple>
        <input type="submit">
      </form>
    
      <div id="sortable">
        <?php displayGalleryItems($clientId, $gallery); ?>
      </div>
    
      <form method="post">
        <input type="hidden" name="order">
        <input type="submit" value="Enregistrer" id="saveBtn">
      </form>
    
      <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
      <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
      <script>
      $(function() {
        $("#sortable").sortable({
          revert: 200,
          update: function() {
            // Every time the order changes, grab the filenames
            var order = $('.pic').map(function(i, p) { return $(p).attr('data-pic'); }).toArray();
            // Set the form input's value
            $('input[name="order"]').val( JSON.stringify(order) );
            // Show the save button
            $('#saveBtn').show();
          }
        });
    
        $(".pic").disableSelection();
      });
      </script>
    </body>
    </html>