90

I am generating a image on client side and I display it with HTML like this:

<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgM...."/>

I want to offer the possibility to download the generated Image.

How can I realize that the browser is opening a file save dialoge (or just download the image like chrome or firefox to the download folder would do) which allows the user to save the image without doing right click and save as on the image?

I would prefer a solution without server interaction. So I am aware that it would be possible if I first upload the Image and then start the download.

Thanks a lot!

alex
  • 4,922
  • 7
  • 37
  • 51

4 Answers4

124

Simply replace image/jpeg with application/octet-stream. The client would not recognise the URL as an inline-able resource, and prompt a download dialog.

A simple JavaScript solution would be:

//var img = reference to image
var url = img.src.replace(/^data:image\/[^;]+/, 'data:application/octet-stream');
window.open(url);
// Or perhaps: location.href = url;
// Or even setting the location of an <iframe> element, 

Another method is to use a blob: URI:

var img = document.images[0];
img.onclick = function() {
    // atob to base64_decode the data-URI
    var image_data = atob(img.src.split(',')[1]);
    // Use typed arrays to convert the binary data to a Blob
    var arraybuffer = new ArrayBuffer(image_data.length);
    var view = new Uint8Array(arraybuffer);
    for (var i=0; i<image_data.length; i++) {
        view[i] = image_data.charCodeAt(i) & 0xff;
    }
    try {
        // This is the recommended method:
        var blob = new Blob([arraybuffer], {type: 'application/octet-stream'});
    } catch (e) {
        // The BlobBuilder API has been deprecated in favour of Blob, but older
        // browsers don't know about the Blob constructor
        // IE10 also supports BlobBuilder, but since the `Blob` constructor
        //  also works, there's no need to add `MSBlobBuilder`.
        var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder);
        bb.append(arraybuffer);
        var blob = bb.getBlob('application/octet-stream'); // <-- Here's the Blob
    }

    // Use the URL object to create a temporary URL
    var url = (window.webkitURL || window.URL).createObjectURL(blob);
    location.href = url; // <-- Download!
};

Relevant documentation

jcubic
  • 61,973
  • 54
  • 229
  • 402
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • 1
    for some reason it seams to be broken in Chrome 19.0 beta but it works on Chrome 18 and Firefox so I am fine. Is there a possibility to set a file name? – alex May 06 '12 at 21:01
  • There is no way to set the file name in a data-URI. Even when a [canvas](https://developer.mozilla.org/en/DOM/HTMLCanvasElement#Methods) / [Blob](http://developer.mozilla.org/en/DOM/Blob) is used to export the data, the file name cannot be set. I've added another method to my answer (I guess that this one will work in Chrome 19). – Rob W May 06 '12 at 21:27
  • 2
    Do you think the first method is going to be deprecated because it is not working in chrome 19 anymore? – alex May 07 '12 at 06:09
  • 1
    I can't find a relevant source on it. I've also found [this answer](http://stackoverflow.com/questions/283956/is-there-any-way-to-specify-a-suggested-filename-when-using-data-uri/6943481#6943481), by the way, which suggests how to set a default name in Chrome (anchors only, the user has to click on it) – Rob W May 07 '12 at 08:02
  • Also found this solutions - seems to be chrome only for now. Thanks for your support! – alex May 07 '12 at 08:21
  • If you need to define a filename, you can also try to set HTTP headers: `` – Nippey Jul 05 '13 at 06:16
  • @Nippey Doesn't work. The `headers` parameter does not exist - http://tools.ietf.org/html/rfc2397 – Rob W Jul 05 '13 at 23:18
  • @RobW: Right, it isn't standard. But this doesn't stop some browsers to interpret this correctly ;) Anyway, Good Luck! – Nippey Jul 08 '13 at 06:21
  • @Nippey Which browser recognizes the parameter? – Rob W Jul 08 '13 at 07:46
  • Opera **did**. I just tried to validate it, but it seems as if Opera removed this in their latest versions. I am very sad now :( – Nippey Jul 08 '13 at 16:01
  • @Nippey I've tested Opera 9 - 15, and none of them support the headers flag. On the bright side, Opera 15 is based on Chromium, so it also supports the "download" attribute. – Rob W Jul 08 '13 at 16:09
  • Perfectly! So, I'll shut up now ^^ – Nippey Jul 08 '13 at 20:06
  • Hmm, the only remnant that I found of this feature is this old discussion. FYI: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html – Nippey Jul 09 '13 at 07:22
  • Excuse me, but how to change format of downloaded image to PNG or JPG? I mean i know i can do it withing operating system - but how to download it as a PNG file? – ScriptyChris Jan 13 '16 at 21:52
  • `Error 404 - We're truly sorry, but there is no such page.` --> Maybe it is time to set a stacksnippet? – Ismael Miguel Mar 18 '16 at 11:12
  • Just wandering how can I set the filename and ext (ex: new_world.jpg) if I'm using the following code: var url = img.src.replace(/^data:image\/[^;]/, 'data:application/octet-stream'); window.open(url); – Jin Yong Aug 03 '17 at 00:37
  • how to specify the download filename? like 'a.png' – Littlee Apr 24 '18 at 09:13
101

you can use the download attribute on an a tag ...

<a href="data:image/jpeg;base64,/9j/4AAQSkZ..." download="filename.jpg"></a>

see more: https://developer.mozilla.org/en/HTML/element/a#attr-download

Bruce Aldridge
  • 2,907
  • 3
  • 23
  • 30
  • 1
    How can I use this solution when I generate the image source dynamically? I mean I have Download button, when user clicks on it, I do some calculations, generate base64 image and ... how can I force downloading? – Mikhail Apr 17 '14 at 12:31
  • In chrome I cannot set the filename using this method. The downloaded file's name remains "download" no matter what. This only happens when using data:image... – cmaduro Jul 17 '14 at 16:53
  • 5
    I know this is an old post but according to W3Schools website 'download' attribute is not supported by IE, Safari and Opera v. < 12 http://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_a_download2 in fact I tried it with IE and it does not work.... :( – Mirko Lugano Nov 26 '14 at 12:09
  • 6
    At the time of this comment, the `download` attribute still isn't supported Safari and IE. – TheCarver Mar 03 '15 at 01:25
  • Where did this "`/9j/4AAQSkZ...`" came from? Is it the image converted to base64? – jofftiquez Jan 06 '16 at 02:58
  • @TheGreenFoxx yes base64 encoded image just copied & pasted from the original question (and shortened) – Bruce Aldridge Jan 06 '16 at 03:12
  • @BruceAldridge another question. How do I create that if my image is just a `URL` ? – jofftiquez Jan 06 '16 at 03:21
  • @TheGreenFoxx this question was specifically in relation to using client side generated images, if you are any other content you can just add the download attribute. If you wish to create a base64 image from a given image you can use many tools online. Just google "base64 image" and you will have lots of options – Bruce Aldridge Jan 07 '16 at 21:44
  • 2
    it have base64 length limitation you can't download larger images using this approch. – Kiran Chenna Jan 25 '19 at 09:35
17

I guess an img tag is needed as a child of an a tag, the following way:

<a download="YourFileName.jpeg" href="data:image/jpeg;base64,iVBO...CYII=">
    <img src="data:image/jpeg;base64,iVBO...CYII="></img>
</a>

or

<a download="YourFileName.jpeg" href="/path/to/OtherFile.jpg">
    <img src="/path/to/OtherFile.jpg"></img>
</a>

Only using the a tag as explained in #15 didn't worked for me with the latest version of Firefox and Chrome, but putting the same image data in both a.href and img.src tags worked for me.

From JavaScript it could be generated like this:

var data = canvas.toDataURL("image/jpeg");

var img = document.createElement('img');
img.src = data;

var a = document.createElement('a');
a.setAttribute("download", "YourFileName.jpeg");
a.setAttribute("href", data);
a.appendChild(img);

var w = open();
w.document.title = 'Export Image';
w.document.body.innerHTML = 'Left-click on the image to save it.';
w.document.body.appendChild(a);
user3521208
  • 171
  • 1
  • 2
  • it's not that it needs an img tag inside, the MSDN docs state that only already downloaded resources can be used - should work as long as you have the image with the same data:base64 anywhere. https://msdn.microsoft.com/en-us/library/cc848897.aspx -- "For security reasons, data URIs are restricted to downloaded resources. Data URIs cannot be used for navigation, for scripting, or to populate frame or iframe elements." – Dimitar Christoff Jun 16 '17 at 14:56
5

Take a look at FileSaver.js. It provides a handy saveAs function which takes care of most browser specific quirks.

jesal
  • 7,852
  • 6
  • 50
  • 56