19

I want to integrate Dropzone.js with a Client Side Image Resizing. I know that there is a function to resize the thumbnail, but I would like to create a function to resize the main image before upload. Anyone could help me please?

Hari Harker
  • 702
  • 1
  • 12
  • 29
Pedro Canelas
  • 301
  • 1
  • 2
  • 4
  • 2
    I'd also like to know if there's an example showing how to use the "Resize" function in dropzone. The doc states: "resize is the function that gets called to create the resize information. It gets the file as first parameter and must return an object with srcX, srcY, srcWidth and srcHeight and the same for trg*. Those values are going to be used by ctx.drawImage()." Would love to see an example. – BenjiFB Mar 29 '14 at 01:32
  • @Satindersingh In this article, the author is resizing the images after the upload, not in the client-side... Thanks for the link anyways. – Bruno Rodrigues Apr 11 '16 at 12:47

5 Answers5

16

Here's how to do it without uploading file from addedfile.

Important thing is to set autoQueue option to false, that way dropzone will not auto upload files selected by a user.

var dropzone = new Dropzone (".dropzone", {
  ...
  autoQueue: false,
  ...
});

Next step is to do resizing and enqueue resized versions in addedfile event.

dropzone.on("addedfile", function(origFile) {
  var MAX_WIDTH  = 800;
  var MAX_HEIGHT = 600;

  var reader = new FileReader();

  // Convert file to img

  reader.addEventListener("load", function(event) {

    var origImg = new Image();
    origImg.src = event.target.result;

    origImg.addEventListener("load", function(event) {

      var width  = event.target.width;
      var height = event.target.height;


      // Don't resize if it's small enough

      if (width <= MAX_WIDTH && height <= MAX_HEIGHT) {
        dropzone.enqueueFile(origFile);
        return;
      }


      // Calc new dims otherwise

      if (width > height) {
        if (width > MAX_WIDTH) {
          height *= MAX_WIDTH / width;
          width = MAX_WIDTH;
        }
      } else {
        if (height > MAX_HEIGHT) {
          width *= MAX_HEIGHT / height;
          height = MAX_HEIGHT;
        }
      }


      // Resize

      var canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;

      var ctx = canvas.getContext("2d");
      ctx.drawImage(origImg, 0, 0, width, height);

      var resizedFile = base64ToFile(canvas.toDataURL(), origFile);


      // Replace original with resized

      var origFileIndex = dropzone.files.indexOf(origFile);
      dropzone.files[origFileIndex] = resizedFile;


      // Enqueue added file manually making it available for
      // further processing by dropzone

      dropzone.enqueueFile(resizedFile);
    });
  });

  reader.readAsDataURL(origFile);
});

Here's a function for converting dataURL to a dropzone file. Process could be simpler if you use canvas.toBlob() instead of canvas.toDataURL(), to get the content of resized file, but latter isn't supported well by all browsers.

It is just modified version of this function.

function base64ToFile(dataURI, origFile) {
  var byteString, mimestring;

  if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
    byteString = atob(dataURI.split(',')[1]);
  } else {
    byteString = decodeURI(dataURI.split(',')[1]);
  }

  mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0];

  var content = new Array();
  for (var i = 0; i < byteString.length; i++) {
    content[i] = byteString.charCodeAt(i);
  }

  var newFile = new File(
    [new Uint8Array(content)], origFile.name, {type: mimestring}
  );


  // Copy props set by the dropzone in the original file

  var origProps = [ 
    "upload", "status", "previewElement", "previewTemplate", "accepted" 
  ];

  $.each(origProps, function(i, p) {
    newFile[p] = origFile[p];
  });

  return newFile;
}
Nabil Kadimi
  • 10,078
  • 2
  • 51
  • 58
rodic
  • 445
  • 4
  • 10
  • Thanks!!! just had to add allow data image and note that the result type is PNG – ozma Jul 03 '16 at 08:28
  • 2
    Better compression can be accomplished by using "jpeg" this way (modify one line): var resizedFile = base64ToFile(canvas.toDataURL("image/jpeg"), origFile); – ozma Jul 03 '16 at 11:44
  • This works really well for me on the desktop and also Android Chrome browsers. However, it doesn't work with iOS Safari. Can anyone please suggest why or perhaps what I can do to get it working? – comphelp Jul 22 '16 at 01:15
  • 2
    I've been troubleshooting this and the line that causes the problem with Safari on iOS is: var newFile = new File( [new Uint8Array(content)], origFile.name, {type: mimestring} ); Presumably it has something to do with iOS's strict permission or lack of a filesystem accessible from a web browser. – comphelp Jul 22 '16 at 05:01
  • @comphelp Thanks for the info! Did you manage to get this working on iOS? – abdullah kahraman Dec 02 '16 at 15:40
  • No. I ended up finding some other code elsewhere in the end. – comphelp Dec 04 '16 at 03:24
  • @comphelp where was the "other code elsewhere"? Thanks – Chris Richardson Jan 26 '17 at 17:40
  • Just replace new File() with new Blob() and put parameters accordingly, worked for me on safari – codeomnitrix Nov 25 '17 at 10:13
15

The Dropzone documentation on the pre-upload resize feature is confusing. The way it reads, you can either limit the width, limit the height, or limit both and sacrifice Aspect Ratio, distorting the image. This is not the case. This:

resizeWidth: 1000, resizeHeight: 1000,
resizeMethod: 'contain', resizeQuality: 1.0,

Limits either width or height to a max of 1000px - whichever is larger. The other will get reduced in accord with the Aspect Ratio, without distorting the image. So for example, in my test I uploaded a 2688x1512 image. Dropzone cropped and resized the Thumbnail to its default 120x120, but the file sent to the server was resized separately by Dropzone, to 1000x562, then sent to the server.

There is an interesting caveat here. JPEGs are going to be recompressed, lossy, so even a resizeQuality of 1.0 is going to result in loss. I see this feature as a method of preventing insanely large files, but you should be careful of resizing twice if you can avoid it (once on server once on client).

If this isn't enough for you - and you really wanted to overload the transform method - it's worth noting that following the code path inside Dropzone is a little confusing, because the createThumbnail codepath is used twice every upload, once to create a thumbnail like you'd expect, and again to pre-resize the image here before passing it to the server. Likewise, the resize method is confusingly named; while resizeWidth etc refer to prepping the image for upload, resize refers to resizing for the thumbnail, and does nothing to the image sent to the server.

Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
  • i uploaded 2 files, one with a width bigger than the height and the other visa-versa. However both were transformed to the same orientation with this code. – luke_mclachlan May 18 '22 at 10:03
9

Dropzone version 5 is recently released, so if you upgrade to dropzone 5, then you can simply use resizeWidth and resizeHeigh to compress your image on the client side.

If you provide only one of them, then dropzone will respect the original aspect ratio, for example if you just add the option: resizeWidth: 800 then your image will be compressed to width=800 pixels and your original image's aspect ration will be respected.

Hooman Bahreini
  • 14,480
  • 11
  • 70
  • 137
  • 1
    Do you know how to resize only images larger than the specified width? Example: `if width > 1000px then resizeWidth: 800, else don't resize ` – little_coder Mar 20 '20 at 02:02
  • 1
    I think you should be able to override the default implementation of `transformFile` and implement your own custom transform logic. According to [documentation](https://www.dropzonejs.com/#config-transformFile): `transformFile: The default implementation uses resizeWidth and resizeHeight (if provided) and resizes images according to those dimensions.`...check the [source code](https://github.com/enyo/dropzone/blob/master/dist/dropzone.js) and it might help you to implement your own custom implementation. – Hooman Bahreini Mar 20 '20 at 02:32
4

It can be done by bypassing the upload done by Dropzone.js. For that, you have to set autoQueue: false in the options (as in the Bootstrap example).

Then you can just send the thumbnail, or you can do it the hard way.


Solution 1: Send the thumbnail

myDropzone.on('thumbnail', function(file, dataURL) {
   $.post('http://example.com', {img: imgToSend})
})

You can define the way you will resize/crop by adding the resize function in the options.


Solution 2: Resize by yourself

There was an other issue: images can need to be rotated (it happens really often). For that, the EXIF orientation tag has to be used. It is apparently lost when you resize, so you have to fetch this information in the frontend, before you resize. For that, I used the Exif.js library.

Here is a working example on JSFiddle. You can see the resized image's data URI in your console.

Karl Horky
  • 4,410
  • 2
  • 31
  • 35
Elzéar
  • 151
  • 5
2

Ok. It took me some and then some more time to find how to implement the resize.

Here is final solution that works for me:

Import css and dropzone.js

<div class="row">
    <div class="col-sm-20 well">
        <form action="@Url.Action("_UploadImages", "Notebooks", new { unique = Model.UniqueId })" 
              method="post" 
              enctype="multipart/form-data"
              class="dropzone"
              id="my-dropzone"></form>
    </div>
</div>

<script type="text/javascript">
    Dropzone.options.myDropzone = {
        url: '@Url.Action("_UploadImages", "Notebooks", new { unique = Model.UniqueId })',
        autoProcessQueue: true,
        uploadMultiple: true,
        parallelUploads: 3,
        maxFilesize: 10,
        resizeWidth: 2048,
        addRemoveLinks: false,
        init: function () {
        }
    }
</script>
HerGiz
  • 907
  • 1
  • 12
  • 21