Send Canvas Content to Your Server
In the previous tutorial I showed you how to draw a line graph using the HTML5 canvas object. In this loosely based follow up tutorial I will show you how to send the canvas image to your server.
Tip
To learn how to draw to the canvas object, check out my previous tutorial here: Draw a Line Graph Using HTML5 Canvas
Step 1. Create The Visual Studio Solution
Start of by creating an ASP.NET Empty Web Application. Add a new Web Form and name it Default.aspx
Download the source from my previous tutorial from here and copy the script tags into the page header and copy the canvas element to the page body. Or if you’re lazy, just copy the code block below.
Add an input with the type button and the ID sendBtn, when the user clicks the button our image will be sent to the server. When you’re finished the ASPX should look a bit like this:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CanvasToServer.Default" %>
<!DOCTYPE html>
<head runat="server">
<title>Canvas To Server</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.min.js"></script>
<script>
var graph;
var xPadding = 30;
var yPadding = 30;
var data = { values: [
{ X: "Jan", Y: 12 },
{ X: "Feb", Y: 28 },
{ X: "Mar", Y: 18 },
{ X: "Apr", Y: 34 },
{ X: "May", Y: 40 },
]
};
// Returns the max Y value in our data list
function getMaxY() {
var max = 0;
for (var i = 0; i < data.values.length; i++) {
if (data.values[i].Y > max) {
max = data.values[i].Y;
}
}
max += 10 - max % 10;
return max;
}
// Return the x pixel for a graph point
function getXPixel(val) {
return ((graph.width() - xPadding) / data.values.length) * val + (xPadding * 1.5);
}
// Return the y pixel for a graph point
function getYPixel(val) {
return graph.height() - (((graph.height() - yPadding) / getMaxY()) * val) - yPadding;
}
$(document).ready(function () {
graph = $('#graph');
var c = graph[0].getContext('2d');
c.lineWidth = 2;
c.strokeStyle = '#333';
c.font = 'italic 8pt sans-serif';
c.textAlign = "center";
// Draw the axises
c.beginPath();
c.moveTo(xPadding, 0);
c.lineTo(xPadding, graph.height() - yPadding);
c.lineTo(graph.width(), graph.height() - yPadding);
c.stroke();
// Draw the X value texts
for (var i = 0; i < data.values.length; i++) {
c.fillText(data.values[i].X, getXPixel(i), graph.height() - yPadding + 20);
}
// Draw the Y value texts
c.textAlign = "right"
c.textBaseline = "middle";
for (var i = 0; i < getMaxY(); i += 10) {
c.fillText(i, xPadding - 10, getYPixel(i));
}
c.strokeStyle = '#f00';
// Draw the line graph
c.beginPath();
c.moveTo(getXPixel(0), getYPixel(data.values[0].Y));
for (var i = 1; i < data.values.length; i++) {
c.lineTo(getXPixel(i), getYPixel(data.values[i].Y));
}
c.stroke();
// Draw the dots
c.fillStyle = '#333';
for (var i = 0; i < data.values.length; i++) {
c.beginPath();
c.arc(getXPixel(i), getYPixel(data.values[i].Y), 4, 0, Math.PI * 2, true);
c.fill();
}
});
</script>
</head>
<body>
<form id="form1" runat="server">
<canvas id="graph" width="200" height="150">
</canvas>
<input type="button" id="sendBtn" value="Send To Server" />
</form>
</body>
</html>
Step 2. Create the JavaScript Function
Next step is to create the JavaScript send function, add the following code to the end of your script tag:
// Send the drawn image to the server
$('#sendBtn').live('click', function () {
var image = graph[0].toDataURL("image/png");
image = image.replace('data:image/png;base64,', '');
$.ajax({
type: 'POST',
url: '/Default.aspx/UploadImage',
data: '{ "imageData" : "' + image + '" }',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (msg) {
alert('Image sent!');
}
});
});
First step is to get the data from the canvas, thats done by calling the toDataURL on the canvas object. Since our saved canvas object is a jQuery object we need to get the regular JavaScript object, that’s done using the “[0]“.
Next we remove the unnecessary header information, otherwise it will make our image invalid when saved. The header is always the same for the type “image/png” so we can remove it using a simple replace.
Then we create a jQuery ajax method to send the data to our WebMethod.
- The type attribute tells jQuery to send it as a HTTP POST.
- The URL is the URL to the page receiving the data combined with the WebMethod to execute.
- The data is the input parameters to our WebMethod, Here we create an input parameter to our WebMethod with the name imageData and the JavaScript image variable as our data.
- The contentType and dataType is the required types for ASP.NET WebMethods.
- Last we have a success event which simple shows an alert box when the image has been successfully sent.
Step 3. Create the Server Side WebMethod And Save the Image
The last step is to create the server side WebMethod and save the image. Go to the code behind for your Default.aspx page and add the following method:
[WebMethod()]
public static void UploadImage(string imageData)
{
FileStream fs = new FileStream("C:\\image.png", FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs);
byte[] data = Convert.FromBase64String(imageData);
bw.Write(data);
bw.Close();
}
First we just add an attribute telling ASP.NET that the method should be able to call from AJAX. Next we create our method, note that the name UploadImage and imageData must be the same as in the url and data parameters in our JavaScript. Then we create a FileStream and a BinaryWriter to write our image to “C:\image.png”.
JavaScript toDataURL encodes the image as a base 64 string we need to convert it back regular byte array, for this we use FromBase64String. Next we just write the data and close our BinaryWriter.
That’s it, debug your project and click “Send to Server” and you should find it in your “C:\”.
Download
Download the source from here: Download
Seems that I’m running into an issue with the png image not saving to my server as a png image file correctly. The only thing I’m doing different is stripping out the “data:image/png;base64,” on the server side.
‘VB Code
imgData = imgData.Substring( imgData.IndexOf(“,”) + 1 )
data= Convert.FromBase64String( imgData )
fs = new FileStream(“image.png”, System.IO.FileMode.Create)
bw = new BinaryWriter(fs)
bw.Write(data)
bw.Close()
A bit weird since if I send the unmodified imgData string directly back to browser and load into image src it displays correctly in the browser – yet if I save it to the server the image it is hosed. Wondering if you are having same issue with the saved png file on your end?
Thanks
After further investigation it looks like the issue is with the server (Windows 2008) and png transparent backgrounds. When opening in MS Paint they show as a black image.
I’ve tried the code on Windows Server 2008 R2, but it differs quite a lot from Windows Server 2008.
I might just be an issue with paint, it doesn’t handle transparency very well, have you tried opening it in another image viewer?
Thanks for sharing your code!
Such good timing.
I was just looking for this exact save to file solution.
You saved me lots of time and it works great.
Thanks so much for the share!
Hi,
thank u for this code. i tried it and work perfect with localhost. however, after hosting my site it is not working. i don’t know why? can you please help me ?
thank you
Hi ravi,
Have you made sure you have write permissions to the folder you’re trying to save it in? Also a good idea is to monitor the request to the server using firebug or a similar debugger.
I need some more information or a page where I can reproduce the issue to help you further.
Good Luck!
Hi, It is very good solution. It is working fine in my local machine but when am hosting my website it does not call the webmethod…can you please help me out?
Hi lasse,
thanks for the reply. i think the problem is not with the writing permission. the issue is that the uploadimage method is not called by ajax function of jquery that we have used for the project. the web method is not called after we host the site on server. do i need to make any changes in the web.config regarding this? also i debug the issue in firebug and i think that it do not get the url : ‘default.aspx/uploadimage’ after hosting.
Hi again,
I’ve had a look at the code and tried it on another server. The only issue I’ve found is that it tries to write the file outside the wwwroot which is not allow (unless the IIS is configured otherwise).
Changing the path in the code behind to:
FileStream fs = new FileStream(HttpContext.Current.Server.MapPath(“/image.png”), FileMode.Create);
Solved it for me, the change will result in that the file is saved to the wwwroot instead of C:\.
Hi lasse,
i have solved the issue from above description. thanks
for helping.
we are now facing a bit issue that is browser specific. this code is working fine on computer with almost all latest browser version. however it is not working on ipad and iphone. can u give any idea?
Is there a way of saving multiple images? I have a canvas where people can draw that is saving the canvas image onto server. Every time someone saves, it replaces the single file on the server with a new image.
But I want to be able to have each image saved under different file names. Any help would be appreciated.
Thanks
Hi Lisa,
That’s quite easy, you just need to make sure the file name is unique so it won’t overwrite the old image.
As you see in this line, it will always use the same name (image.png):
FileStream fs = new FileStream(“C:\\image.png”, FileMode.Create);
You can use the Path.GetRandomFileName() which will give you a not so user friendly but unique file name:
FileStream fs = new FileStream(“C:\\” + Path.GetRandomFileName() + “.png”, FileMode.Create);
You could also append the current date and time or something similar.