问题
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();
}
回答1:
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);
回答2:
I got it. Worked it out as suggested by Kaiido
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
NOTE: Got the above function from HTML5 / Javascript - DataURL to Blob & Blob to DataURL
var downloadCanvas = function(){
var link = document.createElement("a");
var imgData = _canvasObject.toDataURL({ format: 'png',
multiplier: 4});
var strDataURI = imgData.substr(22, imgData.length);
var blob = dataURLtoBlob(imgData);
var objurl = URL.createObjectURL(blob);
link.download = "helloWorld.png";
link.href = objurl;
link.click();
}
回答3:
Since the previous two answers only work for dataURLs that have base64 data, and because this answer has been referenced by more general questions related to "network errors" because of href attributes that are too large, here's the code I'm using:
// must be called in a click handler or some other user action
var download = function(filename, dataUrl) {
var element = document.createElement('a')
var dataBlob = dataURLtoBlob(dataUrl)
element.setAttribute('href', URL.createObjectURL(dataBlob))
element.setAttribute('download', filename)
element.style.display = 'none'
document.body.appendChild(element)
element.click()
var clickHandler;
element.addEventListener('click', clickHandler=function() {
// ..and to wait a frame
requestAnimationFrame(function() {
URL.revokeObjectURL(element.href);
})
element.removeAttribute('href')
element.removeEventListener('click', clickHandler)
})
document.body.removeChild(element)
}
// from Abhinav's answer at https://stackoverflow.com/questions/37135417/download-canvas-as-png-in-fabric-js-giving-network-error/
var dataURLtoBlob = function(dataurl) {
var parts = dataurl.split(','), mime = parts[0].match(/:(.*?);/)[1]
if(parts[0].indexOf('base64') !== -1) {
var bstr = atob(parts[1]), n = bstr.length, u8arr = new Uint8Array(n)
while(n--){
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], {type:mime})
} else {
var raw = decodeURIComponent(parts[1])
return new Blob([raw], {type: mime})
}
}
With these functions you can change the code to this:
document.getElementById("downloadPreview").addEventListener('click', function() {
var dataURL = _canvasObject.toDataURL({format: 'png', multiplier: 4})
download("hellowWorld.png", dataURL)
})
回答4:
I have multiple canvas on my website, and was struggling with same issue in One of them.
Canvas size 1 : 750px x 500px (Network Issue on Window Chrome, works fine on Chrome Macos)
Canvas Size 2: 540px x 1080px (No download issue, works with multiplier 4)
I simply reduced download size, changing multiplier 4 to 2 and it worked fine.
Then I changed multiplier to 3 and it wasn't again working.
then I tried 2.5 and it worked fine again.
Creating such a big file with 4 Multiplier wasn't my requirement, so I settled with 2.5.
This is the easiest and fastest solution to this problem.
来源:https://stackoverflow.com/questions/37135417/download-canvas-as-png-in-fabric-js-giving-network-error