40

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.

user192318
  • 493
  • 2
  • 6
  • 6

5 Answers5

35

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.

Jayesh
  • 51,787
  • 22
  • 76
  • 99
  • the img argument will be cut if it's too big, have you ever met that? – ernest Sep 25 '12 at 09:47
  • @ernest I have search for the post restrictions that you mentioned (I knew about get request limitation) and I don't find that http://www.w3schools.com/tags/ref_httpmethods.asp W3school says "POST requests have no restrictions on data length" – ccsakuweb Feb 12 '14 at 07:28
  • The limitation is in the configuration of the server. See quota limitations https://developers.google.com/appengine/docs/quotas?hl=es&csw=1 – ccsakuweb Feb 12 '14 at 07:38
  • @ccsakuweb Regarding post length limitations, I wrote a canvas signature routine a few years back. By storing each line into an array, I was able to post the data points to the server and recreate server side when the data was posted. I'll see if I can dig up the code for that when I get to work. – B2K May 05 '14 at 13:52
  • Sending raw image data is not very bandwidth efficient, no? Would be better to convert it into a dataUrl and send that. – joerx Sep 17 '15 at 04:19
  • @PerHornshøj-Schierbeck : I meant worked smooth as butter :P – Surya Nov 25 '15 at 17:28
11

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.

Sumner Evans
  • 8,951
  • 5
  • 30
  • 47
Fabien Ménager
  • 140,109
  • 3
  • 41
  • 60
  • Did anyone got this to actually work? I got that data returned from getImageData() can be POSTed to the server, but I can't figure out how. I tried using different options in jQuery.ajax and jQuery.post, but I couldn't figure how to specify image data in ajax request. My server can't interpret the data I send. I couldn't find any code examples either. Do you have any pointers? (I made sure server is working correct by doing the same request with Curl as client.) Thanks. – Jayesh Jul 14 '10 at 18:16
  • 3
    It's possible to used it on IE with the fxCanvas (not with ExCanvas). You need to used the fonction toDataURL("image/png", function (fxUrl) {...}). – remi.gaubert Dec 03 '11 at 09:40
  • The resource cited in this answer no longer exists. – Jim Jeffers Dec 25 '13 at 01:56
  • 8
    Link 404s, that's why the guidelines state you should include code in your answer, not just links. – Greg Jan 09 '14 at 10:33
  • Take the data and base64 it for the upload –  Feb 22 '17 at 01:28
  • 1
    getImageData returns the raw pixel data, uncompressed, not sure this is the right answer – Martijn Scheffer Mar 12 '17 at 19:22
8

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);
Firze
  • 3,939
  • 6
  • 48
  • 61
3

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();
}
B2K
  • 2,541
  • 1
  • 22
  • 34
0

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

Jeffrey Aylesworth
  • 8,242
  • 9
  • 40
  • 57