user192318
user192318

Reputation: 503

Uploading 'canvas' image data to the server

I need to upload the canvas image data to the server (database) on the fly, i.e., I need to create a form and with an input=file, and post the image data without any user interaction.

Upvotes: 41

Views: 65404

Answers (5)

Fabien Ménager
Fabien Ménager

Reputation: 140195

You don't need a file input, just get the data with ctx.getImageData() and post it to the server with Ajax.

See the MDN Documentation for CanvasRenderingContext2D.getImageData().

But you won't be able to get the image data in IE, even with ExCanvas.

Upvotes: 11

Firze
Firze

Reputation: 4049

Here is how I solved this. Posting the image as base64 array using JavaScript and then decoding and saving it as a image using PHP.

Client side (JavaScript):

$.post('/ajax/uploadthumbnail',
    {
        id : id,
        img : canvas.toDataURL("image/png")
    }, function(data) {
        console.log(data);
    });

Server side (PHP):

$img = $_POST['img'];
$img = str_replace('data:image/png;base64,', '', $img);
$img = str_replace(' ', '+', $img);
$data = base64_decode($img);
$file = $_SERVER['DOCUMENT_ROOT'] . '/images/some_name.png';
file_put_contents($file, $data);

Upvotes: 8

B2K
B2K

Reputation: 2611

Here is a demo of an online signature app that I wrote last year Canvas Signature Demo. This has the advantage of posting only the vector data to the server. With all of the path info, you could also apply smoothing algorithms or scale it as needed before persisting.

<canvas id="signature" width="300" height="100"></canvas>
<form method="post" id="signature_form" action="signing.aspx">
<input type="hidden" name="paths" id="paths"/>
    <p><label>Cover #</label> <input type="text" id="covernumber" name="covernumber"/>
    <input type="submit" id="save" value="Save"/>
</form>

I store the path data into a hidden field and post that to the server.

signature.js Core logic below:

mouseDown: function(event) {
    var point = this.getRelativePoint(event);
    this.paths.push( [ point ] ); 
    this.ctx.fillRect(point.x,point.y,1,1);
    this.penDown = true;
    this.updateField();
},
mouseUp: function(event) {
    this.penDown = false;
    this.ctx.closePath();
    if ( Prototype.Browser.IE && event.srcElement.tagName != "INPUT" ) {
        var ver = getInternetExplorerVersion();
        if ( ver >= 8 && ver < 9 && document.selection ) {
            document.selection.empty();
        }
    }
},
mouseMove: function(event) {
    if ( this.penDown ) {
        var lastPath = this.paths[ this.paths.length - 1 ];
        var lastPoint = lastPath[ lastPath.length - 1 ];
        var point = this.getRelativePoint(event);
        lastPath.push( point );
        this.ctx.strokeStyle = "#000000";
        this.ctx.beginPath();
        this.ctx.moveTo(lastPoint.x,lastPoint.y);
        this.ctx.lineTo(point.x, point.y);
        this.ctx.stroke();
        this.ctx.closePath();
        this.updateField();
    }
},
updateField: function() {
    if ( this.field ) {
        this.field.value = this.paths.toJSON();
    }
}

Here is my relevant server side .Net code (C#).

if ( Request("paths") ) {
    var objBitmap : Bitmap = new Bitmap(300, 100);
    var objGraphics : Graphics = Graphics.FromImage(objBitmap);
    objGraphics.Clear(Color.Transparent);
    objGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

    var paths:Array = eval(Request("paths")) || [];
    var p : int;
    var q : int;
    var path : Array;

    for ( p = 0; p< paths.length; p++ ) {
        var path = paths[p];
        if ( path.length == 1 ) {
            objGraphics.DrawRectangle(new Pen(Color.Black), path[0].x, path[0].y, 1, 1);
        } else {
          for ( q = 1; q<path.length; q++ ) {
              var prev = path[q-1];
              var curr = path[q];
              objGraphics.DrawLine(new Pen(Color.Black), parseInt(prev.x),parseInt(prev.y),parseInt(curr.x),parseInt(curr.y));
          }
        }
    }
    objBitmap.Save("C:\\temp\\" + Request("covernumber") + ".png", ImageFormat.Png);
    objBitmap.Dispose();
    objGraphics.Dispose();
}

Upvotes: 3

Jayesh
Jayesh

Reputation: 53335

FWIW, this is how I got it working.

My server is in google app engine. I send canvas.toDataURL()'s output as part of post request using jQuery.post. The data URL is base64 encoded image data. So on server I decode it and convert it to image

import re 
import base64
class TnUploadHandler(webapp.RequestHandler):
    dataUrlPattern = re.compile('data:image/(png|jpeg);base64,(.*)$')
    def post(self):
        uid = self.request.get('uid')
        img = self.request.get('img')

        imgb64 = self.dataUrlPattern.match(img).group(2)
        if imgb64 is not None and len(imgb64) > 0:
            thumbnail = Thumbnail(
                    uid = uid, img = db.Blob(base64.b64decode(imgb64)))
            thumbnail.put()

From client I send the data like this:

$.post('/upload',
        {
            uid : uid,
            img : canvas.toDataURL('image/jpeg')
        },
        function(data) {});

This may not be the best way to do it, but it works.

Upvotes: 35

Jeffrey Aylesworth
Jeffrey Aylesworth

Reputation: 8470

You can get the image data in the form of a data: url, this only works in Firefox and Opera so far though.

http://cow.neondragon.net/index.php/681-Canvas-Todataurl

Upvotes: 0

Related Questions