Canvas.toDataURL() для большого холста

У меня проблема с .toDataURL() для большого холста. Я хочу заключить код в base64 и декодировать в php файле, но если у меня есть большой холст, переменная strDataURI пуста.

Мой код:

var strDataURI = canvas.toDataURL();
strDataURI = strDataURI.substr(22, strDataURI.length);
$.post("save.php",
{ 
   str: strDataURI
};

Есть ли альтернатива .toDataURL() или какой-либо способ изменить ограничение размера?

Спасибо.

Ответ 1

Я не уверен, есть ли ограничения размеров холста, но URL-адреса данных имеют ограничения в зависимости от браузера: Ограничения по размеру URL-адресов.

Что бы вы могли попробовать, используйте Node.js + node -canvas (серверная сторона), чтобы воссоздать холст. Я использовал их для создания печатаемых изображений из элементов холста, и до сих пор не было никаких проблем/ограничений, использующих toDataURL.

Используете ли вы библиотеку fabric.js? Я заметил, что вы отправили на свой форум. Fabric.js можно использовать в Node.js и имеет метод toDataURLWithMultiplier, который масштабирует холст/контекст, позволяющий вам изменить dataurl Размер изображения. Вы можете проверить источник метода, чтобы узнать, как это делается.

Edit:

Поскольку вы используете fabric.js, я бы предложил использовать Node.js для обработки холста для обработки изображений на сервере. Вы найдете более подробную информацию о том, как использовать fabric.js на Node.js здесь.

Вот простой сервер, использующий Node.js и выражающий:

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

Ответ 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

Ответ 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. 
  }
}

Ответ 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");
        });

  }