Download Canvas as PNG in fabric.js giving network Error

Abhinav picture Abhinav · May 10, 2016 · Viewed 14.7k times · Source

I want to download Canvas as PNG using fabric.js. While downloading I want to scale the image. So I use multiplier property of toDataURL() function. But I get Failed-Network Error

PS: If I dont give multiplier property,it is downloading but I do want to use multiplier property since I have to scale the image

This is what I am doing:

HTML Code:

<canvas width="400" height="500" id="canvas" ></canvas>
 <a id='downloadPreview' href="javascript:void(0)"> Download Image </a>

JS

document.getElementById("downloadPreview").addEventListener('click', downloadCanvas, false);

var _canvasObject = new fabric.Canvas('canvas');

var downloadCanvas =    function(){
    var link = document.createElement("a");

link.href = _canvasObject.toDataURL({format: 'png', multiplier: 4});
      link.download = "helloWorld.png";
     link.click();

}

Answer

Kaiido picture Kaiido · May 11, 2016

The problem you are facing is not directly related to fabricjs, (nor canvas and not even javascript btw), but comes from limitations some browsers (including Chrome) does have on the maximum length for the src attribute of an Anchor Element (<a>) with the donwload attribute.

When this limit is reached, then the only thing you've got is this uncatchable "Network Error" in the console ; the download as failed, but you as the developer can't know about it.

As proposed in this (you-refused-to-mark-as-) duplicate, the solution is either to directly get a Blob when available (for canvas, you may call its toBlob() method, or to first convert your dataURI to a Blob, and then create an object URL from this Blob.

Fabricjs doesn't seem to have a toBlob function implemented yet, so in your exact case, you'll have to do the later.
You can find many scripts to convert dataURI to Blob, one is available in MDN's polyfill to Canvas.toBlob() method.

Then it would look like this :

// edited from https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Polyfill
function dataURIToBlob(dataURI, callback) {
  var binStr = atob(dataURI.split(',')[1]),
    len = binStr.length,
    arr = new Uint8Array(len);

  for (var i = 0; i < len; i++) {
    arr[i] = binStr.charCodeAt(i);
  }

  callback(new Blob([arr]));
}

var callback = function(blob) {
    var a = document.createElement('a');
    a.download = fileName;
    a.innerHTML = 'download';
    // the string representation of the object URL will be small enough to workaround the browser's limitations
    a.href = URL.createObjectURL(blob);
    // you must revoke the object URL, 
    //   but since we can't know when the download occured, we have to attach it on the click handler..
    a.onclick = function() {
      // ..and to wait a frame
      requestAnimationFrame(function() {
          URL.revokeObjectURL(a.href);
        });
        a.removeAttribute('href')
      };
    };

dataURIToBlob(yourDataURL, callback);