Salomón Charabati

October 4, 2023

Support HEIC images in a Rails app using minimagick, a custom Rails Previewer and dropzone.js [Part 2 - Frontend]

[Part 2 of 2]

Following Part 1 of this series, we'll now have a look at a different use case.

Let's say we have a user signup, where users upload images of their driver's license. In our example, we'll be using Dropzone to have users upload their images. Let's look at how to add support to HEIC uploads.

What we want to achieve is to give the user an immediate preview of the HEIC file the user is uploading. To do this, we used an open source library called Heic2any, which is a client side library for converting HEIC files to JPEG, PNG and GIF. In short, transform client-side HEIC files into a readable format for the browser.

Here's how we can make this happen:


In our createDropzone function, where we return a new Dropzone zone, we do the following. Besides from the basic configuration of the Dropzone zone, such as maxFiles, maxFilesSize, etc, we add .heic extension to the acceptedFiles configuration.

function createDropZone(componentId) {
  return new Dropzone(`#${componentId}`, {
    url: "/signup",
    maxFiles: 1,
    maxFilesize: 5,
    maxThumbnailFilesize: 5,
    acceptedFiles: ".jpg, .jpeg, .png, .heic",
    thumbnailHeight: null,
    hiddenInputContainer: `#${componentId}`,
    init: function() {
      this.on("success", function(file) {
        $('.dz-image').css({"width":"100%", "height":"auto"});
        let blobURL = URL.createObjectURL(file);
        convertHeicImage(blobURL, file)
      })
    }
  }
}


Let's think our case for a moment. In the success event by Dropzone, after the image has been uploaded, it will do the conversion to a readable file by the browser, in our case jpeg file.

In order to pull this off, we'll do our call to heic2any on the success event by Dropzone.

let convertHeicImage = function(blobURL, file){
  fetch(blobURL)
    .then((res) => res.blob())
    .then((blob) => heic2any({
      blob,
      quality: 0.1,
			toType: 'image/jpeg'
    }))
    .then((conversionResult) => {
      var url = URL.createObjectURL(conversionResult);
      let dropzoneZone = file.controller.fileInput.dataset.target.split('.')[1]
      appendConvertedImageToElement(dropzoneZone, url)
    })
    .catch((e) => {
      console.log(e);
    });
}


What are we doing in the piece of code above? 👆

In essence, this is just the call to heica2any, passing it the blobURL created with the following call:

 let blobURL = URL.createObjectURL(file)


As for the following bit, our use case was for multiple Dropzone Zones in the user's form. Therefore, this part:

let dropzoneZone = file.controller.fileInput.dataset.target.split('.')[1]
appendConvertedImageToElement(dropzoneZone, url)


It's useful to know that the fileInput which Dropzone is now handling, is stored in the file's controller. Quite helpful when you are working with more than one Dropzone zone :)

That's pretty much it. Now, we're able to accept HEIC images in our Rails app, use a custom Rails previewer and integrate heic2any using Dropzone.js.