1

I'm converting images to base64 using canvas. What i need to do is convert those images and then show the result to the user (original image and base64 version). Everything works as expected with small images, but when i try to convert large images (>3MB) and the conversion time increases, the base64 version is empty. This might be is caused because the result is shown before the toDataURL() function is completed.

I need to show the result after all the needed processing has ended, for testing purposes.

Here's my code:

var convertToBase64 = function(url, callback)
{
    var image = new Image();

    image.onload = function ()
    {
        //create canvas and draw image...

        var imageData = canvas.toDataURL('image/png');          
        callback(imageData);
    };

    image.src = url;
};


convertToBase64('img/circle.png', function(imageData)
{
    window.open(imageData);
});

Even though i'm using image.onload() with a callback, i'm unable to show the result after the toDataURL() has been processed.

What am i doing wrong?

UPDATE: I tried both the solutions below and they didn't work. I'm using AngularJS and Electron in this project. Any way i can force the code to be synchronous? Or maybe some solution using Promises?

UPDATE #2: @Kaiido pointed out that toDataURL() is in fact synchronous and this issue is more likely due to maximum URI length. Since i'm using Electron and the image preview was for testing purposes only, i'm going to save the file in a folder and analise it from there.

Ricky
  • 2,912
  • 8
  • 47
  • 74
  • 1
    *"This is caused because the result is shown before the toDataURL() function is completed."* No, `toDataURL` method is synchronous. This is more likely due to maximum URI length for location, what browser are you using ? Why do you need a base64 version ? If it's only to open the image in a new page, then prefer a [blobURI](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) ([`msSaveorOpenBlob`](https://msdn.microsoft.com/en-us/library/hh772332(v=vs.85).aspx) for IE) and the [`canvas.toBlob`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) method. – Kaiido Mar 14 '17 at 01:27
  • Thanks for the reply. since this apps is running in Electron, i really have to use Chrome. I wanted the dataURL because i thought it would be the fastest way to view the result image (for testing purposes). Anyway, i changed my approach, instead of showing the image in a new window, i'm saving it to a file. – Ricky Mar 14 '17 at 09:29
  • 1
    No toDataURL is slower than toBlob, and is synchronous (may freeze your screen). toBlob should always be prefered. chrome does support toBlob and blobURIs (created with `URL.createObjectURL(blob)`) – Kaiido Mar 14 '17 at 09:36
  • I initially went with that approach because i was using small images and everything was working seamlessly. Thanks for the tips :) – Ricky Mar 14 '17 at 09:41

2 Answers2

1

Your code seems absolutely fine. Not sure why isn't working you. Maybe, there are some issues with your browser. Perhaps try using a different one. Also you could use a custom event, which gets triggered when the image conversion is competed.

// using jQuery for custom event

function convertToBase64(url) {
    var image = new Image();
    image.src = url;
    image.onload = function() {
        var canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;
        var ctx = canvas.getContext('2d');
        ctx.drawImage(image, 0, 0);
        var imageData = canvas.toDataURL();
        $(document).trigger('conversionCompleted', imageData);
    };
};

convertToBase64('4mb.jpg');
$(document).on('conversionCompleted', function(e, d) {
    window.open(d);
});
m87
  • 4,445
  • 3
  • 16
  • 31
  • 1
    Your image.src line should be after the definition of image.onload. – fmacdee Mar 13 '17 at 20:52
  • 2
    There appear to be many posts relating to this. The concept is that if an image is cached then it can load immediately before the execution moves forward to setup the image.onload, and thus the onload will never fire. Here is one such post but there are several... http://stackoverflow.com/questions/14648598/is-it-necessary-to-set-onload-function-before-setting-src-for-an-image-object – fmacdee Mar 13 '17 at 21:06
  • @siam thanks for the suggestion. I'm using Electron which runs on Chrome. Your solution didn't work, is there any other option i can try? I'm using AngularJS if that helps... – Ricky Mar 13 '17 at 23:10
1

This approach might work for you. It shows the image onscreen using the native html element, then draws it to a canvas, then converts the canvas to Base64, then clears the canvas and draws the converted image onto the canvas. You can then scroll between the top image (original) and the bottom image (converted). I tried it on large images and it takes a second or two for the second image to draw but it seems to work...

Html is here:

<img id="imageID">
<canvas id="myCanvas" style="width:400;height:400;">
</canvas>

Script is here:

var ctx;
function convertToBase64(url, callback)
{
  var image = document.getElementById("imageID");
  image.onload = function() {
    var canvas = document.getElementById("myCanvas");
    canvas.width = image.naturalWidth;
    canvas.height = image.naturalHeight;
    ctx = canvas.getContext("2d");
    ctx.drawImage(image,0,0);
    var imageData = canvas.toDataURL('image/png');
    ctx.fillStyle ="#FFFFFF";
    ctx.fillRect(0,0,canvas.width,canvas.height);
    callback(imageData);
  };
  image.src = url;
};

var imagename = 'images/bigfiletest.jpg';

window.onload = function () {
  convertToBase64(imagename, function(imageData) {
    var myImage = new Image();
    myImage.src = imageData;
    ctx.drawImage(myImage,0,0);    
  });
}

Note that I also tried it without the callback and it worked fine as well...

fmacdee
  • 2,353
  • 10
  • 15
  • Thanks for the help. Unfortunately your solution didn't work either. Any other alternative i could try? I'm using AngularJS and Electron if it helps. – Ricky Mar 13 '17 at 23:18