canvas.toDataURL() 用于大画布
Posted
技术标签:
【中文标题】canvas.toDataURL() 用于大画布【英文标题】:canvas.toDataURL() for large canvas 【发布时间】:2013-04-15 21:40:58 【问题描述】:我对大画布的.toDataURL()
有疑问。我想在base64
中编码并在 php 文件上解码,但如果我有一个大画布,strDataURI
变量是空的。
我的代码:
var strDataURI = canvas.toDataURL();
strDataURI = strDataURI.substr(22, strDataURI.length);
$.post("save.php",
str: strDataURI
;
是否有任何替代.toDataURL()
或更改大小限制的方法?
谢谢。
【问题讨论】:
哪个浏览器,还是全部? IE 对数据 URL 的大小有限制。 您使用的是什么浏览器,您的画布实际尺寸是多少? 我的浏览器是谷歌浏览器,但我在其他浏览器中测试过。我不知道我的画布尺寸是多少,但尺寸是 20000x20000 像素。谢谢。 一张 20000x20000 像素的图像会产生一个大小约为 2 GB 的数据 URL。 edit 等待,更像是 4 GB,因为 javascript 字符串使用 UTF-16。 在这种情况下,使用 Blob 而不是数据 uri 可能会更好;htmlCanvasElement.toBlob
【参考方案1】:
我不确定画布尺寸是否有限制,但数据 url 有限制,具体取决于浏览器:Data URL size limitations。
您可以尝试使用 Node.js + node-canvas(服务器端)重新创建画布。我一直在使用这些从画布元素创建可打印图像,并且到目前为止使用 toDataURL 没有任何问题/限制。
您使用的是 fabric.js 库吗?我注意到你也在他们的论坛上发帖。 Fabric.js 可以在 Node.js 中使用,并且有一个 toDataURLWithMultiplier 方法,它可以缩放画布/上下文,允许您更改 dataurl 图像大小。您可以检查方法源以了解这是如何完成的。
编辑:
由于您使用的是 fabric.js,我建议您使用 Node.js 来处理画布以在服务器上进行图像处理。您将在 Node.js here 上找到有关如何使用 fabric.js 的更多信息。
这是一个使用 Node.js 和 express 的简单服务器:
var express = require('express'),
fs = require('fs'),
fabric = require('fabric').fabric,
app = express(),
port = 3000;
var allowCrossDomain = function (req, res, next)
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
app.configure(function()
app.use(express.bodyParser());
app.use(allowCrossDomain);
);
app.options('/', function(req, res)
res.send(200);
);
app.post('/', function(req, res)
var canvas = fabric.createCanvasForNode(req.body.width, req.body.height);
console.log('> Loading JSON ...');
canvas.loadFromJSON(req.body.json, function()
canvas.renderAll();
console.log('> Getting PNG data ... (this can take a while)');
var dataUrl = canvas.toDataURLWithMultiplier('png', req.body.multiplier),
data = dataUrl.replace(/^data:image\/png;base64,/, '');
console.log('> Saving PNG to file ...');
var filePath = __dirname + '/test.png';
fs.writeFile(filePath, data, 'base64', function(err)
if (err)
console.log('! Error saving PNG: ' + err);
res.json(200, error: 'Error saving PNG: ' + err );
else
console.log('> PNG file saved to: ' + filePath);
res.json(200, success: 'PNG file saved to: ' + filePath );
);
);
);
app.listen(port);
console.log('> Server listening on port ' + port);
当服务器运行时,您可以向它发送数据 (postData
)。
服务器期望json
、width
和height
重新创建画布,并希望multiplier
缩放数据url 图像。客户端代码如下所示:
var postData =
json: canvas.toJSON(),
width: canvas.getWidth(),
height: canvas.getHeight(),
multiplier: 2
;
$.ajax(
url: 'http://localhost:3000',
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(postData),
dataType: 'json',
success: function(data)
console.log(data);
,
error: function(err)
console.log(err);
);
【讨论】:
感谢您的回复。 Node.js 只能在 Unix 服务器上运行,对吧? 您可以在 Node.js download page 上找到 Windows 和 MacOSX 安装程序 是的。我的项目涉及将非常大的画布导出为 jpg 或 png 图像。但我认为画布不支持大尺寸。你推荐我任何选择?我正在使用像 1000x1000(例如)这样的小画布,然后我需要通过 Imagick 缩放画布对象以转换为 300 DPI。谢谢。 我已经用服务器和客户端示例更新了我的答案。希望对您有所帮助。【参考方案2】:您应该首先考虑这一点:上传的大小是有限的。该限制取决于浏览器、操作系统和服务器环境。你可以看看这篇文章:http://www.motobit.com/help/scptutl/pa98.htm
一般来说,您可以尝试这样的事情: 首先我们需要一个函数将 dataURI 转换为 blob:
function convertDataURItoBlob(dataURI)
'use strict'
var byteString,
mimestring
if(dataURI.split(',')[0].indexOf('base64') !== -1 )
byteString = atob(dataURI.split(',')[1])
else
byteString = decodeURI(dataURI.split(',')[1])
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]
var content = new Array();
for (var i = 0; i < byteString.length; i++)
content[i] = byteString.charCodeAt(i)
var rawContent = new Uint8Array(content),
returnBlob = new Blob([rawContent], type: mimestring)
return returnBlob;
还有一个上传文件的函数,使用 XMLHttpRequest2:
function upload(blob)
var xhr = new XMLHttpRequest();
xhr.open('POST', '/yourServerEndPoint', true);
xhr.onload = function(e) ... ;
xhr.send(blob);
现在您可以将strDataURI
传递给第一个函数,然后使用第二个函数上传文件。
您可以在此处深入了解 XMLHTTPRequest2:http://www.html5rocks.com/en/tutorials/file/xhr2/ 关于这里的 blob 构造函数:https://developer.mozilla.org/en-US/docs/DOM/Blob
【讨论】:
我不需要上传。我需要将我的画布转换为 PNG。我的问题是导出画布。例如:我有一个 100x100 厘米、72 DPI 分辨率的画布,但我需要调整到 300 dpi。最终画布约为 30000x30000 像素(100 厘米 * 300 约为 30000 像素),并且 toDataURL 函数将不起作用。我认为该问题与 【参考方案3】:您总是可以将图像分成更小的部分并单独保存,这可能不是一个坏主意。基本上你会有一个类似的功能
var largeCanvas = document.getElementById('yourGiantCanvas').getContext('2d'),
slice = document.createElement('canvas').getContext('2d');
slice.canvas.width = 1000;
slice.canvas.height = 1000;
for (var y=0; y < canvas.height; y+=1000)
for (var x=0; x < canvas.width; x+=1000)
slice.clearRect(0, 0, slice.canvas.width, slice.canvas.height);
slice.drawImage(largeCanvas.canvas, x, y, 1000, 1000, 0, 0, 1000, 1000);
var imagePiece = slice.canvas.toDataURL();
//Now just save the imagePiece however you normally were planning to
//and you can build the image again using these slices. You can create
//a much better user experience this way too.
【讨论】:
你有什么问题?这不是一个完整的工作功能,只是一个模板,说明你将如何做这样的事情。 请抱歉,这可能有效。但我需要你的帮助。如何解析 imagePiece var 或数组来创建一个像这样的图像: $.post("createImage.php", str: imagePiece; ; 如何加入所有的 imagePiece 来制作一个像这样的脚本: $urlUploadImages = 'images /'; $nameImage = 'test.png'; $data = base64_decode($_POST["str"]); $img = imagecreatefromstring($data); $width = imagesx($img); ... imagedestroy($ img); 非常感谢。 我不会费心加入 php 中的所有图像(除非你必须这样做,如果你确实检查了这个***.com/questions/1719909/…)。我只会以某种命名格式保存所有图像,即 ImagePiece001.png、ImagePiece002.png 等......然后当您将它们加载回前端时,只需创建一个大画布并将这些碎片拼凑在一起。【参考方案4】:已更新代码以将画布拆分为更小的画布对象。效果很好,还添加了一个跟踪器:
这允许跟踪上传过程,我认为总体上对用户来说更好。我稍后会使用 PHP 重新加入。
它避免了画布/浏览器等大小的问题。
我的第一篇文章希望对你有帮助!
// 传入文件名的类型
function sliceCanvas(type, canvasId)
var largeCanvas = document.getElementById(canvasId).getContext('2d');
var slice = document.createElement('canvas').getContext('2d');
var baseSize = 500;
fileH = largeCanvas.canvas.height / baseSize;
fileW = largeCanvas.canvas.width / baseSize;
slice.canvas.width = baseSize;
slice.canvas.height = baseSize;
count = 1;
numFiles = Math.ceil(fileH) * Math.ceil(fileW);
for (var y=0; y < largeCanvas.canvas.height; y+=baseSize)
for (var x=0; x < largeCanvas.canvas.width; x+=baseSize)
slice.clearRect(0, 0, slice.canvas.width, slice.canvas.height);
slice.drawImage(largeCanvas.canvas, x, y, baseSize, baseSize, 0, 0, baseSize, baseSize);
var imagePiece = slice.canvas.toDataURL();
typeFinal = type + count;
exportSlice(typeFinal, imagePiece, numFiles);
count++;
要上传的 Ajax:
function exportSlice(type, dataURL, fileNum)
percent = 0;
percentComplete = 0;
$.ajax(
type: "POST",
url: YourServerSideFiletoSave,
data: image: dataURL, type: type
)
.done(function( response )
console.log(response);
percent++;
percentComplete = Math.ceil(Number(percent/fileNum*100));
return true;
)
.fail(function(response)
console.log("Image FAILED");
console.log(response);
return false;
)
.always(function(response)
console.log( "Always");
);
【讨论】:
以上是关于canvas.toDataURL() 用于大画布的主要内容,如果未能解决你的问题,请参考以下文章
canvas.toDataURL() 抛出安全异常,尽管图像是本地的
为啥我在html5中取到了canvas画布(已测试能取到),但是执行canvas.toDataURL("image.png")不成功,