HTML5 Canvas to PNG File

Info: IE10+ doesn’t support below method at all. Other people already did the work and implemented cross browser solutions.
This is one of them.

First, add the generated data URL to the href attribute of an <a> tag.
However on some browsers, this alone will not trigger a download. Instead it will open the linked image in a new page.

Download dialog for a base64 image:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...." class="image" />

Based on above example, convert the MIME type of the data URL to this:

<a href="https://stackoverflow.com/questions/12796513/data:application/octet-stream;base64,iVBORw0KGgoAAAANSUhEUg....">Download</a>

By telling the browser that the data are application/octet-stream, it will ask you to save it on your hard-disk.


Specifying a filename:

As Adi said in the comments below, there is no standard way to define a filename using this method. But, there are two approaches which might work in some browsers.

A) The download attribute introduced by Google Crome

<a download="image.png" href="https://stackoverflow.com/questions/12796513/...">

B) Defining HTTP headers within the data URL
headers=Content-Disposition: attachment; filename=image.png

<a href="https://stackoverflow.com/questions/12796513/data:application/octet-stream;headers=Content-Disposition%3A%20attachment%3B%20filename=image.png;base64,iVBORw0KGgoAAAA">

This worked at least in some older versions of Opera.
Here is some discussion about this.

Looking into the Bug/Feature-Tracking systems of the major browsers shows that defining a filename is quite a big wish of the community. Maybe we will see a cross-browser compatible solution in near future! 😉


Save RAM and CPU ressources:

If you don’t want to bloat the RAM of your visitor’s browser, you can also generate the data URL dynamically:

<a id="dl" download="Canvas.png">Download Canvas</a>
function dlCanvas() {
    var dt = canvas.toDataURL('image/png');
    this.href = dt;
};
dl.addEventListener('click', dlCanvas, false);

This way, your canvas may still be shown as an image file by your browser.
If you want to increase the probability to open a download dialog, you should extend the function above, so that it does the replacement as shown above:

function dlCanvas() {
    var dt = canvas.toDataURL('image/png');
    this.href = dt.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
};
dl.addEventListener('click', dlCanvas, false);

Last, add the HTTP header to make extra sure that most browsers offer a valid filename to you! 😉


FULL EXAMPLE:

var canvas = document.getElementById("cnv");
var ctx = canvas.getContext("2d");

/* FILL CANVAS WITH IMAGE DATA */
function r(ctx, x, y, w, h, c) {
  ctx.beginPath();
  ctx.rect(x, y, w, h);
  ctx.strokeStyle = c;
  ctx.stroke();
}
r(ctx, 0, 0, 32, 32, "black");
r(ctx, 4, 4, 16, 16, "red");
r(ctx, 8, 8, 16, 16, "green");
r(ctx, 12, 12, 16, 16, "blue");

/* REGISTER DOWNLOAD HANDLER */
/* Only convert the canvas to Data URL when the user clicks. 
   This saves RAM and CPU ressources in case this feature is not required. */
function dlCanvas() {
  var dt = canvas.toDataURL('image/png');
  /* Change MIME type to trick the browser to downlaod the file instead of displaying it */
  dt = dt.replace(/^data:image\/[^;]*/, 'data:application/octet-stream');

  /* In addition to <a>'s "download" attribute, you can define HTTP-style headers */
  dt = dt.replace(/^data:application\/octet-stream/, 'data:application/octet-stream;headers=Content-Disposition%3A%20attachment%3B%20filename=Canvas.png');

  this.href = dt;
};
document.getElementById("dl").addEventListener('click', dlCanvas, false);
<canvas id="cnv" width="32" height="32"></canvas>
<a id="dl" download="Canvas.png" href="#">Download Canvas</a>

Leave a Comment