在fabric.js中将画布下载为PNG,出现网络错误

IT技术 javascript html canvas fabricjs
2021-02-01 01:54:24

我想使用 fabric.js 将 Canvas 下载为 PNG。下载时我想缩放图像。所以我使用函数的multiplier属性toDataURL()但我收到失败的网络错误

PS:如果我不提供multiplier财产,它正在下载但我确实想使用multiplier财产,因为我必须缩放图像

这就是我正在做的:

HTML代码:

<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();

}
5个回答

您面临的问题与fabricjs(也不是canvas,甚至不是javascript btw)没有直接关系,而是来自某些浏览器(包括Chrome)确实对带有donwloadsrc的Anchor Element ( <a>)属性的最大长度的限制属性。

当达到这个限制时,你唯一得到的就是控制台中这个无法捕捉的“网络错误”;下载失败,但您作为开发人员无法知道。

正如在这个(you-refused-to-mark-as-) duplicate 中提出的,解决方案是在可用时直接获取 Blob(对于画布,您可以调用它的toBlob()方法,或者首先将您的 dataURI 转换为 Blob,并且然后从这个 Blob创建一个对象 URL

Fabricjs 似乎toBlob尚未实现功能,因此在您的确切情况下,您必须稍后执行。
您可以找到许多将 dataURI 转换为 Blob 的脚本,其中一种在MDN 的 polyfill toCanvas.toBlob()方法中可用

然后它看起来像这样:

// 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);
@BT 你知道从光栅图像文件(或从画布)获取 dataURI 的很多其他方法吗?只有文本文件(例如 svg 等标记语言)才能以非 b64 编码方式 (URL_encoded) 表示,否则无法表示二进制数据。这个问题是关于将 fabricjs 画布导出为 PNG(因此是光栅和二进制)。
2021-03-15 01:54:24
另一件应该注意的事情是:dataURIToBlob 仅适用于 base64 编码的 dataURI。
2021-03-21 01:54:24
您的 dataURIToBlob 格式不正确。在哪里定义类型或质量?并且this最有可能引用窗口。
2021-03-25 01:54:24
@BT,你说得对,我不知道它怎么会在没有人注意到的情况下保持这么长时间(我是第一个)。谢谢。但是我可能应该删除这个 dataURIToBlob 函数,因为它toBlob已经在大多数主要浏览器中实现了,它比转换两倍的数据要快得多。
2021-03-30 01:54:24
这个问题是由更一般的问题指出的,所以它会帮助我知道你的答案不一般。我敢肯定它对canvas的东西很好!
2021-04-14 01:54:24

我知道了。按照 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});
}

注意:从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();
} 
我有这段代码在 Chrome 和 Edge 中运行良好,但在 FF 中没有任何反应。你知道这在 FF 中对你有用吗?
2021-03-16 01:54:24
我今天休息,所以我现在不能检查和告诉你,但我想它在 FF 中有效,我会尽快再次检查
2021-03-28 01:54:24
惊人的。有同样的问题并用您的解决方案解决了它。非常感谢!
2021-04-02 01:54:24

由于前两个答案仅适用于具有 base64 数据的 dataURL,并且由于 href 属性太大,此答案已被与“网络错误”相关的更一般问题引用,因此这是我正在使用的代码:

// 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})
    }
}

使用这些函数,您可以将代码更改为:

document.getElementById("downloadPreview").addEventListener('click', function() {
  var dataURL = _canvasObject.toDataURL({format: 'png', multiplier: 4})
  download("hellowWorld.png", dataURL)
})
不应该重新打开链接的问题吗?(至少如果它已关闭)这并没有提供关于当前问题的太多有用信息:“在 fabric.js 中将画布下载为 PNG”。除了通过 base64 编码二进制数据之外,没有其他方法可以获取表示 PNG 文件的 dataURI。
2021-04-04 01:54:24

我的网站上有多个画布,并且在其中之一中遇到了同样的问题。

画布尺寸 1:750px x 500px(Window Chrome 上的网络问题,在 Chrome Macos 上工作正常)

画布尺寸 2:540 像素 x 1080 像素(无下载问题,适用于乘数 4)

我只是减少了下载大小,将乘数 4 更改为 2,效果很好。

然后我将乘数更改为 3,但它不再起作用。

然后我尝试了 2.5,它再次正常工作。

用 4 Multiplier 创建这么大的文件不是我的要求,所以我选择了 2.5。

这是解决此问题的最简单和最快的方法。

就我而言,我只是使用了这个

 var callback = function(blob) {
var a = document.createElement('a');
var saveAs = $('input[name="extensionGrp"]:checked').val();


var link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'Image.' + saveAs;
document.body.appendChild(link);
link.click();

};