在fabric.js中将Canvas下载为PNG,给出网络错误

Posted

技术标签:

【中文标题】在fabric.js中将Canvas下载为PNG,给出网络错误【英文标题】:Download Canvas as PNG in fabric.js giving network Error 【发布时间】:2016-09-05 05:57:15 【问题描述】:

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

PS:如果我不给multiplier属性,它正在下载,但我想使用multiplier属性,因为我必须缩放图像

这就是我正在做的:

html 代码:

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


【问题讨论】:

您是否仅在 chrome 上出现此错误?如果是这样,这可能是相关的:***.com/questions/36918075/… 但对于您的用例,而不是调用 toBlob(我认为在 fabricjs 中不可用),您必须在设置锚点之前将 dataURI 转换为 blob href 指向此 blob 的对象 URL。 (您可以查看MDN's polyfill for toBlob 并选择他们将 dataURI 转换为 blob 的方式) 我必须将 href 设置为 blob 值吗? 拒绝从 blob (URL.createObjectURL(blob)) 创建的对象 URL。检查建议的欺骗的答案。 兄弟......它有效,非常感谢你,从昨天开始我一直在挠头。 blob 和 ObjectUrl 是怎么回事?为什么它有效而 dataUri 无效? 好吧,当您调整画布大小时,输出的 dataURL 太大以至于超出了 Chrome 对具有 download 属性的锚点 (&lt;a&gt;) 的限制。我不知道为什么他们确实有这个限制。此外,如果它确实解决了您的问题,您可以接受重复的 ;-) 【参考方案1】:

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

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

;

【讨论】:

【参考方案2】:

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

画布尺寸 1:750px x 500px(Window Chrome 上的网络问题,在 Chrome Macos 上运行良好)

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

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

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

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

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

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

【讨论】:

【参考方案3】:

由于前两个答案仅适用于具有 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://***.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。【参考方案4】:

您面临的问题与fabricjs没有直接关系(也不是canvas,甚至不是javascript btw),而是来自某些浏览器(包括Chrome)对锚的src属性的最大长度的限制具有 donwload 属性的元素 (&lt;a&gt;)。

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

正如 (you-refused-to-mark-as-) duplicate 中提出的,解决方案是在可用时直接获取 Blob(对于画布,您可以将其称为 @ 987654322@ 方法,或者先将你的 dataURI 转换为 Blob,然后从这个 Blob 创建一个object URL。

Fabricjs 似乎还没有实现 toBlob 函数,所以在你的确切情况下,你必须稍后再做。 您可以找到许多将 dataURI 转换为 Blob 的脚本,其中一个在 MDN's polyfill 到 Canvas.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);

【讨论】:

您的 dataURIToBlob 格式不正确。类型或质量在哪里定义?而this 最有可能引用该窗口。 @BT,你是完全正确的,我不知道它怎么能保持这么长时间没有人注意到它(我是第一个)。谢谢。但我可能应该删除这个 dataURIToBlob 函数,因为 toBlob 已经在大多数主要浏览器中使用,它比转换两倍的数据要快。 关于这一点还有一点需要注意:dataURIToBlob 仅适用于 base64 编码的 dataURI。 @BT 你知道从光栅图像文件(或画布)获取 dataURI 的许多其他方法吗?只有文本文件(例如 svg 等标记语言)才能以非 b64 编码方式(URL_encoded)表示,二进制数据不能以其他方式表示。这个问题是关于将 fabricjs 画布导出为 PNG(因此是光栅和二进制)。 这个问题是由更一般的问题所指向的,所以它会帮助我知道你的答案不是一般的。我敢肯定它适合帆布的东西!【参考方案5】:

我明白了。按照 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 中是否有用吗? 我今天休息,所以我现在不能检查并告诉你,但我想它在 FF 中有效,我会尽快再次检查 太棒了。有同样的问题并用您的解决方案解决了它。非常感谢!

以上是关于在fabric.js中将Canvas下载为PNG,给出网络错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在fabric js中覆盖canvas.add()方法

在fabric.js中用颜色填充图像

Fabric.JS 与 Node.JS - 导出为 PNG/JPEG

在使用canvas api在浏览器中将svg转换为png时,svg中的嵌入图像在Safari中随机空白

canvas库fabric.js踩坑

Fabric.js:100% 宽度的画布可能吗?