Uploading Captured Canvas Images

Monday, January 7, 2013

In "Capturing HTML 5 Video To An Image", Chris asked if we could upload a captured image to the server.

Yes we can!

We will use the same markup from the previous post, but make a simple change to the script. Instead of creating an image on the client, we'll send the image to the server by passing the dataURL generated from the canvas.

(function() {
    "use strict";
    var video, $output;
    var scale = 1;

    var initialize = function() {
        $output = $("#output");
        video = $("#video").get(0);
        $("#capture").click(captureImage);                
    };

    var captureImage = function() {
        var canvas = document.createElement("canvas");
        canvas.width = video.videoWidth * scale;
        canvas.height = video.videoHeight * scale;
        canvas.getContext('2d')
              .drawImage(video, 0, 0,
                         canvas.width, canvas.height);

        $.post("/image/upload", { dataUrl: canvas.toDataURL() })
         .done(showImage);
    };

    var showImage = function(url) {
        var img = document.createElement("img");
        img.src = url;
        $output.prepend(img);
    };

    $(initialize);            

}());

On the server we need to parse the incoming data URL. The data URL for an image will contain base 64 encoded bits, like the following:

data:image/png;base64,F12VBORw0KGgoAAAAN...==

The ImageDataUrl class will use a regex to pick apart the string and decode the bytes inside.

public class ImageDataUrl
{
    public ImageDataUrl(string dataUrl)
    {            
        var match = _regex.Match(dataUrl);
        MimeType = match.Groups["mimeType"].Value;
        Format = match.Groups["mimeSubType"].Value;
        Bytes = Convert.FromBase64String(match.Groups["data"].Value);
    }

    public byte[] Bytes { get; protected set; }
    public string MimeType { get; set; }
    public string Format { get; protected set; }
    
    public string SaveTo(string folder)
    {
        var fileName = Guid.NewGuid().ToString() + "." + Format;
        var fullPath = Path.Combine(folder, fileName);

        using(var file = File.OpenWrite(fullPath))
        {
            file.Write(Bytes, 0, Bytes.Length);
        }
        return fileName;
    }

    private static readonly Regex _regex = new Regex(
        @"data:(?<mimeType>[\w]+)/(?<mimeSubType>[\w]+);\w+,(?<data>.*)",
        RegexOptions.Compiled
    );
}   

The ImageDataUrl class can also save the decoded bytes to disk, so all we need to do inside of a controller action is use the class, and return a URL to the newly saved image.

[HttpPost]
public string Upload(string dataUrl)
{
    var image = new ImageDataUrl(dataUrl);
    var fileName = image.SaveTo(Server.MapPath("~/uploads"));
    var url = Url.Content("~/uploads/" + fileName);
    return url;
}

That's one approach to uploading the image.


Comments
gravatar Khalid Abuhakmeh Monday, January 7, 2013
That's very cool, what are you using this technique in specifically?
gravatar Scott Monday, January 7, 2013
@Khalid: an experiment for annotating videos.
gravatar Tyrone Monday, January 7, 2013
Nice post! I like the way you "modularized" the functionality by using the self invoking function. I think you have an extra ending parenthesis at the end (last line of code before the semi colon). I've seen it on this post and the one prior to this.
gravatar Fredi Machado Monday, January 7, 2013
@Scott: Thanks for this code! @Tyrone: in fact, the parenthesis is just misplaced, it should close just after the closing curly braces.
gravatar Scott Monday, January 7, 2013
@Tyrone, @Fredi: It's a matter of preference, but I prefer: (function() { }()); to: (function() { })(); Both are legal, however.
gravatar Tyrone Monday, January 7, 2013
Cool. I didn't know that. Looks like Crockford has something to say on this as well. :-) https://twitter.com/paul_irish/status/176187448420864000
gravatar Fredi Machado Tuesday, January 8, 2013
I didn't know that too. Thanks for the explanation Scott.
Comments are closed.

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!