62

I am trying to convert an HTML5 canvas to an image. This is what I got so far:

var tmp_canvas = document.getElementById('canvas');
var dataURL = tmp_canvas.toDataURL("image/png");
$('#thumbnail_list').append($('<img/>', { src : dataURL }).addClass('image'));

but the problem is that I get this code:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAEsCAYAAADtt+XCAAAgAElEQVR4nNS6Z1xVaZbvv/c+CVOZc6mYEMlJMZRizgljGRARs6AgOSMGQATBSM5ZyTkoOQkSzJWrp3t6etLt6Z7pmf/c++L7f3EOiBZW2dM9dz73xfdzztl7n3Oe/Txrrd9a69mCTC4gkwvIZAKSTECUBARRQBA+jii+46f.......class="image">

I want a normal image path that the user can download!

Any help?

Rob
  • 5,223
  • 5
  • 41
  • 62
Test Test
  • 2,831
  • 8
  • 44
  • 64
  • 1
    What you are getting is base64 representation of that canvas to image, I think you would have to covert it to bytes and then save those bytes into an image file that you want. – yogi Oct 09 '12 at 09:11
  • You could add a simple prompt to tell the user to right click on the generated thumbnail and hit 'save target as' – boz Oct 09 '12 at 09:11

4 Answers4

94

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="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="...">

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

<a href="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>
Community
  • 1
  • 1
Nippey
  • 4,708
  • 36
  • 44
  • 4
    Unfortunately you cannot specify a file name this way. – Adi Oct 09 '12 at 09:23
  • Unfortunately, it did not work on Safari Version 7.0.3 (9537.75.14) Mac OS X – Miguel Mota Mar 31 '15 at 00:31
  • Hi @Moogs, unfortunately, I have no chance to test on a Mac, but I'm always curious ;) Could you provide more details, like: Does the canvas render the three coloured boxes? Do you get output on the error console? Do you get a download dialogue? Anyway, Safari _7.0.3 is exactly one year old_. Officially, there is no full HTML5 support for Safari <7.1. – Nippey Mar 31 '15 at 11:27
  • @Nippey the canvas renders the same on chrome and safari. The only difference is clicking on download link in safari does nothing, and the link disappears. No console errors. – Miguel Mota Mar 31 '15 at 19:04
  • When you say the link disappears, you mean the formatting? This gives a hint, that an empty string has been assigned. So either .toDataURL returns nothing/unexpected or .replace kills the string. Hm, I really like to dig into such problems, but doing it remote is difficult :) – Nippey Apr 01 '15 at 06:04
  • @Adi To set a filename just add `this.download = "whatever.png";` as the last line of `dlCanvas` function. – Peter Kovac May 11 '16 at 11:15
  • The code snippet worked on my Safari on MAC - however, the name of the image downloaded shows as unknown. Chrome, however, downloads "Canvas.png" – software.wikipedia Nov 16 '16 at 19:14
  • Thanks for the comment, yes the `download` attribute only works for chromium-based browsers like Chrome and Opera. Other browsers don't seem to support custom file names on Client-Side yet. – Nippey Nov 16 '16 at 19:44
  • 1
    This works for me for Chrome and Firefox. However completely fails on IE11 (Nothing downloaded, no dialog shown). – beginner_ Dec 21 '16 at 11:36
  • Welcome to the World of M$. :) IE10+ don't support above method. There are solutions covering all major browsers like this one: `https://github.com/rndme/download` – Nippey Dec 22 '16 at 06:53
  • for IE, you can try using msToBlob and msSaveBlob: http://stackoverflow.com/a/28819615/1231466 – danbrellis Feb 24 '17 at 19:17
  • When i try to download the canvas image then the image which gets downloaded is broken or damaged – Samarth Kejriwal May 05 '18 at 05:17
13

You can use the reimg library to convert the canvas to an image object, and even trigger the download for the user.

After you insert the library in your page, just use this : ReImg.fromCanvas(yourCanvasElement).downloadPng()

gillyb
  • 8,760
  • 8
  • 53
  • 80
5

You have 2 options (both work on almost all browsers):

1- POST the data to the server:
On the server you'd have a script that will handle the data and then tell the browser to prompt the user for download.

header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=something.png");
header("Content-Type: image/png");
echo base64_decode($_POST['imageData']);
exit;

2- Prompt the user for download using Downloadify

<div id="clickMe"></div>

Downloadify.create( 'clickMe', {
   data: base64String,
   dataType: 'base64',
   filename: 'something.png'
});
Adi
  • 5,089
  • 6
  • 33
  • 47
-1

You might also consider using Concrete.js http://www.concretejs.com, which is a light weight HTML5 Canvas framework that enables peripheral things like this, including downloads. You would just do this:

canvas.download({
  fileName: 'my-file.png'
});

and you're done.

Eric Rowell
  • 5,191
  • 23
  • 22
  • 2
    what is that canvas? how to create it ? is it a Concrete object? is it a html element ?how to initialize it to have the download function from your library ? what version of concrete is this? is there a CDN for it? – Belun Mar 19 '19 at 16:34