用于大型画布的canvas.toDataURL()

用于大型画布的canvas.toDataURL(),canvas,html5-canvas,fabricjs,Canvas,Html5 Canvas,Fabricjs,我对大型画布的.toDataURL()有问题。我想在base64中编码并在php文件上解码,但是如果我有一个大画布,strDataURI变量是空的 我的代码: var strDataURI = canvas.toDataURL(); strDataURI = strDataURI.substr(22, strDataURI.length); $.post("save.php", { str: strDataURI }; 除了.toDataURL()或以某种方式更改大小限制之外,还有其他

我对大型画布的
.toDataURL()
有问题。我想在
base64
中编码并在php文件上解码,但是如果我有一个大画布,
strDataURI
变量是空的

我的代码:

var strDataURI = canvas.toDataURL();
strDataURI = strDataURI.substr(22, strDataURI.length);
$.post("save.php",
{ 
   str: strDataURI
};
除了
.toDataURL()
或以某种方式更改大小限制之外,还有其他方法吗


谢谢。

你首先要考虑的是:上传的大小是有限的。限制取决于浏览器、操作系统和服务器环境。您可以看看这篇文章:

通常,您可以尝试以下方法: 首先,我们需要一个函数将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;

}
现在,您可以将您的
strDataURI
传递给第一个函数,然后使用第二个函数上载文件

您可以在这里更深入地了解XMLHTTPRequest2:
关于blob构造器:

您可以将图像分成更小的部分,然后单独保存,这可能不是一个坏主意。基本上你会有一个函数

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. 
  }
}
var largeCanvas=document.getElementById('yourGiantCanvas').getContext('2d'),
slice=document.createElement('canvas').getContext('2d');
slice.canvas.width=1000;
slice.canvas.height=1000;
对于(变量y=0;y
我不确定画布维度是否有限制,但数据URL有限制,具体取决于浏览器:

您可以尝试使用Node.js+节点画布(服务器端)重新创建画布。我一直在使用这些工具从画布元素创建可打印的图像,到目前为止,使用toDataURL没有任何问题/限制

您正在使用fabric.js库吗?我注意到你也在他们的论坛上发了帖子。 Fabric.js可以在Node.js中使用,并且有一个方法,可以缩放画布/上下文,允许您更改dataurl图像大小。您可以检查方法源以了解如何完成此操作

编辑: 由于您使用的是fabric.js,我建议使用Node.js在服务器上处理画布到图像的处理。您将在Node.js上找到有关如何使用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
来重新创建画布,并需要
乘数来缩放数据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);
    }
});

已更新代码以将画布拆分为更小的画布对象。效果相当不错,还添加了一个跟踪器:

这允许跟踪上传过程,总体而言,我认为对用户来说更好。我在稍后阶段使用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++;


  } 
}
}

哪个浏览器,或者是所有浏览器?IE对数据URL的大小有限制。您使用的浏览器是什么,画布的实际大小是什么?我的浏览器是Google Chrome,但我已经在其他浏览器中进行了测试。我不知道画布的大小,但尺寸是20000x2000像素。谢谢。一个20000x2000像素的图像将产生一个大约2GB大小的数据URL。编辑等待,更像4G字节,因为JavaScript字符串使用UTF-16;我不需要上传。我需要在PNG转换我的画布。我的问题是导出画布。例如:我有一个像100x100厘米的画布,分辨率为72 DPI,但我需要调整到300 DPI。最终的画布大约为30000x30000像素(100厘米*300大约为30000像素),toDataURL函数将不起作用。我认为这个问题与尺寸限制有关,像这样谢谢你的回复。谢谢你的回复。Node.js仅在Unix服务器上工作,对吗?您可以在“是”上找到Windows和MacOSX安装程序。我的项目涉及导出非常大的画布到jpg或png图像。但我认为画布不支持大尺寸。你有什么建议吗?我在小画布上工作,比如1000x1000(例如),然后我需要通过Imagick将画布对象缩放到300 DPI。谢谢。我已经用服务器和客户端的例子更新了我的答案。希望有帮助。你有什么问题?这不是一个完整的工作功能,只是一个模板,你将如何做这样的事情。请抱歉,可能这是工作。但我需要你的帮助。如何解析imagePiece变量或数组以创建一个如下图像:$.post(“createImage.php”,{str:imagePiece;};如何连接所有imagePieces以生成一个如下脚本所示的图像:$urlUploadImages='images/';$nameImage='test.png';$data=base64_decode($\u post[“str”]);$img=imagecreatefromstring($data)$width=imagesx($img);…imagedestroy($img);非常感谢。我不会费心在php中加入所有图像(除非你必须这样做,如果你真的签出了这个)。我只需将所有图像以某种命名格式保存,即ImagePiece001.png、ImagePiece002.png等。然后,当您将它们加载回前端时,只需创建一个大画布并将这些片段平铺在一起。
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++;


  } 
}
}
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");
        });

  }