Using cropper.js before Dropzone.js send image to server

6 min read 07-10-2024
Using cropper.js before Dropzone.js send image to server


Cropping Images Before Uploading with Cropper.js and Dropzone.js

Modern web applications often involve users uploading images, but it's not always ideal to allow them to upload large, unedited images. Cropping images before upload helps optimize storage and bandwidth, improves user experience, and ensures uploaded images fit specific requirements. This article will demonstrate how to combine the power of Cropper.js for image cropping and Dropzone.js for drag-and-drop uploads to create a smooth and efficient image upload process.

The Scenario:

Imagine you are building a profile picture upload feature. You want users to be able to easily drag and drop images, but you also want to ensure the uploaded images are a specific size and aspect ratio.

Here's a basic example of how Dropzone.js might be used without any cropping functionality:

<!DOCTYPE html>
<html>
<head>
  <title>Image Upload</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.0/min/dropzone.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.0/min/dropzone.min.js"></script>
</head>
<body>
  <div id="dropzone" class="dropzone"></div>

  <script>
    Dropzone.options.dropzone = {
      url: "/upload",
      maxFiles: 1,
      acceptedFiles: "image/*",
      init: function () {
        this.on("success", function (file, response) {
          // Handle successful upload
          console.log("File uploaded successfully:", response);
        });
      }
    };
  </script>
</body>
</html>

This code sets up Dropzone.js to allow users to drag-and-drop files, but it doesn't handle cropping.

Integrating Cropper.js for Image Pre-Processing

To incorporate cropping functionality, we'll use Cropper.js, a lightweight JavaScript library that provides a user-friendly image cropping interface.

Here's how to integrate Cropper.js with Dropzone.js:

  1. Include Cropper.js: Add the Cropper.js library to your HTML file. You can download it from https://github.com/fengyuanchen/cropperjs or use a CDN link like:

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js"></script>
    
  2. Modify Dropzone.js: Modify your Dropzone.js configuration to capture the image data after a file is added. Inside the Dropzone.js initialization, add:

    this.on("addedfile", function(file) {
      // Create a preview image for cropping
      var preview = document.createElement('img');
      preview.src = URL.createObjectURL(file); 
      preview.id = 'cropImage';
      preview.classList.add('img-fluid'); // Apply a CSS class to style the image
    
      // Create a container for the cropped image
      var imageContainer = document.createElement('div');
      imageContainer.id = 'imageContainer';
      imageContainer.classList.add('my-container'); // Add a class to style the container
      imageContainer.appendChild(preview);
      
      // Add the container to the Dropzone
      this.element.appendChild(imageContainer);
    });
    
  3. Initialize Cropper.js: After the file is added and previewed, initialize Cropper.js:

    this.on("addedfile", function(file) {
      // ... (previous code) ...
      
      var cropper = new Cropper(preview, {
        aspectRatio: 16 / 9, // Set your desired aspect ratio
        viewMode: 1, // Choose how the image is displayed (1: show image in the canvas)
        dragMode: 'move', // Control how the image can be manipulated by the user
        cropBoxMovable: true,
        cropBoxResizable: true, 
        zoomable: false, // Disable image zooming by default
      });
    });
    
  4. Handle Crop and Upload: When the user confirms the crop, capture the cropped image data and send it to your server.

    // Add a button to trigger the cropping process
    var cropButton = document.createElement('button');
    cropButton.textContent = 'Crop Image';
    cropButton.classList.add('btn', 'btn-primary');
    
    // Add an event listener to trigger the cropping process
    cropButton.addEventListener('click', function () {
      // Get the cropped image data 
      var croppedImage = cropper.getCroppedCanvas({
        width: 300 // Set the desired width of the cropped image
      }).toDataURL();
      
      // Replace the original image with the cropped image in the Dropzone
      preview.src = croppedImage;
    
      // Remove the cropping interface
      cropper.destroy(); 
    
      // Create a file object from the cropped image data
      var croppedFile = dataURItoBlob(croppedImage);
      croppedFile.name = file.name;
    
      // Send the cropped image to the server
      this.files.push(croppedFile);
      this.processQueue(); // Process the queue for upload
    });
    
    // Add the crop button to the Dropzone
    this.element.appendChild(cropButton);
    
    // ... (rest of your Dropzone.js initialization) ...
    

Helper Function (dataURItoBlob): This function is used to convert the data URI obtained from cropper.getCroppedCanvas() into a Blob object, which is the format required by Dropzone.js for upload.

function dataURItoBlob(dataURI) {
  // Convert data URI to Blob
  var byteString = atob(dataURI.split(',')[1]);
  var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
  var ab = new ArrayBuffer(byteString.length);
  var ia = new Uint8Array(ab);
  for (var i = 0; i < byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  }
  return new Blob([ab], { type: mimeString });
}

Complete Example:

<!DOCTYPE html>
<html>
<head>
  <title>Image Upload with Cropping</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.0/min/dropzone.min.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.0/min/dropzone.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js"></script>
  <style>
    .my-container {
      display: flex;
      flex-direction: column;
      align-items: center;
      margin-bottom: 20px;
    }
  </style>
</head>
<body>
  <div id="dropzone" class="dropzone"></div>

  <script>
    Dropzone.options.dropzone = {
      url: "/upload",
      maxFiles: 1,
      acceptedFiles: "image/*",
      init: function () {
        this.on("addedfile", function(file) {
          var preview = document.createElement('img');
          preview.src = URL.createObjectURL(file);
          preview.id = 'cropImage';
          preview.classList.add('img-fluid'); 
          var imageContainer = document.createElement('div');
          imageContainer.id = 'imageContainer';
          imageContainer.classList.add('my-container');
          imageContainer.appendChild(preview);
          this.element.appendChild(imageContainer);

          var cropper = new Cropper(preview, {
            aspectRatio: 16 / 9,
            viewMode: 1,
            dragMode: 'move',
            cropBoxMovable: true,
            cropBoxResizable: true,
            zoomable: false
          });

          var cropButton = document.createElement('button');
          cropButton.textContent = 'Crop Image';
          cropButton.classList.add('btn', 'btn-primary');

          cropButton.addEventListener('click', function () {
            var croppedImage = cropper.getCroppedCanvas({ width: 300 }).toDataURL();
            preview.src = croppedImage;
            cropper.destroy();
            
            var croppedFile = dataURItoBlob(croppedImage);
            croppedFile.name = file.name;

            this.files.push(croppedFile);
            this.processQueue(); 
          });

          this.element.appendChild(cropButton);
        });

        this.on("success", function (file, response) {
          // Handle successful upload
          console.log("File uploaded successfully:", response);
        });
      }
    };

    // Helper function to convert data URI to Blob
    function dataURItoBlob(dataURI) {
      var byteString = atob(dataURI.split(',')[1]);
      var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
      var ab = new ArrayBuffer(byteString.length);
      var ia = new Uint8Array(ab);
      for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }
      return new Blob([ab], { type: mimeString });
    }
  </script>
</body>
</html>

Explanation:

  • Dropzone.js: Handles the drag-and-drop functionality and uploads the cropped file to the server.
  • Cropper.js: Provides the user interface for cropping images.
  • Cropping and Upload: The script initializes Cropper.js when a file is added to Dropzone, creating a cropping interface for the user. When the user clicks the 'Crop Image' button, the script gets the cropped image data, converts it to a Blob object, and adds it to the Dropzone queue for upload.

Key Benefits:

  • Improved User Experience: Cropping allows users to easily adjust their images before upload.
  • Optimized Images: Cropping reduces file sizes, saving storage space and improving website performance.
  • Customizable Image Dimensions: You can set the desired aspect ratio and dimensions for uploaded images.
  • Efficient Upload Process: Cropping can reduce the size of the uploaded file, making the upload process faster.

Further Enhancements:

  • Preview Image Styling: You can add CSS to the preview image for styling.
  • Error Handling: Implement error handling for scenarios like invalid file types or failed uploads.
  • Server-Side Validation: Validate the uploaded image dimensions and format on the server to prevent malicious uploads.
  • Image Optimization: Consider using image optimization libraries on the server side to further reduce file sizes.

By combining the power of Cropper.js and Dropzone.js, you can create a user-friendly and efficient image upload experience that ensures optimal image quality and performance.