在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
属性的锚点 (<a>
) 的限制。我不知道为什么他们确实有这个限制。此外,如果它确实解决了您的问题,您可以接受重复的 ;-)
【参考方案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 属性的元素 (<a>
)。
当达到这个限制时,你唯一得到的就是控制台中这个无法捕获的“网络错误”;下载失败,但您作为开发人员不知道。
正如 (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 与 Node.JS - 导出为 PNG/JPEG