37

I have converted the source content from the <img> html tag to a base64String using JavaScript. The image was displayed clearly. Now I want to save that image to user's disk using javascript.

<html>
    <head>
    <script>
        function saveImageAs () {
            var imgOrURL;
            embedImage.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA" +
                             "AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO" +
                             "9TXL0Y4OHwAAAABJRU5ErkJggg==";
            imgOrURL = embedImage;
            if (typeof imgOrURL == 'object')
                imgOrURL = embedImage.src;
            window.win = open(imgOrURL);
            setTimeout('win.document.execCommand("SaveAs")', 0);
        }
    </script>
    </head>
    <body>
        <a href="#" ONCLICK="saveImageAs(); return false" >save image</a>

        <img id="embedImage" alt="Red dot">
    </body>
</html>

This code worked well when I set the image path as source for <img> html tag. However, when I pass the source as base64String does not work.

How to achieve what I want?

ChesuCR
  • 9,352
  • 5
  • 51
  • 114
vengatesh
  • 503
  • 1
  • 6
  • 15
  • I know there are limits about usage of data:image in IE... file size and other. Try to look on it before. – Fabio B. Oct 31 '11 at 08:45
  • if it has limits ,then it should not have been appeared in the screen. but the image is displayed even for long sized images . i couldn't be able to save that image tom disk . – vengatesh Oct 31 '11 at 08:58
  • can i pass bytearray as source for tag using javascript? – vengatesh Oct 31 '11 at 09:41

4 Answers4

53

HTML5 download attribute

Just to allow user to download the image or other file you may use the HTML5 download attribute.

Static file download

<a href="/images/image-name.jpg" download>
<!-- OR -->
<a href="/images/image-name.jpg" download="new-image-name.jpg"> 

Dynamic file download

In cases requesting image dynamically it is possible to emulate such download.

If your image is already loaded and you have the base64 source then:

function saveBase64AsFile(base64, fileName) {
    var link = document.createElement("a");

    document.body.appendChild(link); // for Firefox

    link.setAttribute("href", base64);
    link.setAttribute("download", fileName);
    link.click();
}

Otherwise if image file is downloaded as Blob you can use FileReader to convert it to Base64:

function saveBlobAsFile(blob, fileName) {
    var reader = new FileReader();

    reader.onloadend = function () {    
        var base64 = reader.result ;
        var link = document.createElement("a");

        document.body.appendChild(link); // for Firefox

        link.setAttribute("href", base64);
        link.setAttribute("download", fileName);
        link.click();
    };

    reader.readAsDataURL(blob);
}

Firefox

The anchor tag you are creating also needs to be added to the DOM in Firefox, in order to be recognized for click events (Link).

IE is not supported: Caniuse link

Community
  • 1
  • 1
Andrii Verbytskyi
  • 7,155
  • 3
  • 47
  • 38
25

In JavaScript you cannot have the direct access to the filesystem. However, you can make browser to pop up a dialog window allowing the user to pick the save location. In order to do this, use the replace method with your Base64String and replace "image/png" with "image/octet-stream":

"data:image/png;base64,iVBORw0KG...".replace("image/png", "image/octet-stream");

Also, W3C-compliant browsers provide 2 methods to work with base64-encoded and binary data:

Probably, you will find them useful in a way...


Here is a refactored version of what I understand you need:

window.addEventListener('DOMContentLoaded', () => {
  const img = document.getElementById('embedImage');
  img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA' +
    'AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO' +
    '9TXL0Y4OHwAAAABJRU5ErkJggg==';

  img.addEventListener('load', () => button.removeAttribute('disabled'));
  
  const button = document.getElementById('saveImage');
  button.addEventListener('click', () => {
    window.location.href = img.src.replace('image/png', 'image/octet-stream');
  });
});
<!DOCTYPE html>
<html>

<body>
  <img id="embedImage" alt="Red dot" />
  <button id="saveImage" disabled="disabled">save image</button>
</body>

</html>
John Doe
  • 4,574
  • 2
  • 26
  • 30
  • 2
    Note that `atob` and `btoa` have never been part of any standard. They are supported by *some* standards-compliant browsers, but don't count on it being in all of them. – Andy E Oct 31 '11 at 10:30
  • @AndyE Correct. That's why you have to perform feature testing before using these methods. – John Doe Oct 31 '11 at 10:36
  • @John Doe : still i couldn't get the save dialogue box for saving the image to user's disk – vengatesh Oct 31 '11 at 11:58
  • 1
    @vengatesh : please see the updated answer. keep in mind that you'll have to specify the correct file extension in order to have your image saved properly. – John Doe Oct 31 '11 at 12:58
  • 1
    @John Doe:thanks alot . but it doesn't work with IE ,can we get the file location from from user? can we have the same file format as before?. because all are saved as .part file. – vengatesh Oct 31 '11 at 13:48
  • @vengatesh : that's right, you have to specify the **full** filename including an extension (e.g., png). could you please clarify your objective? what do you mean by "_can we get the file location from from user? can we have the same file format as before?_"? – John Doe Oct 31 '11 at 14:52
  • @Johm Doe:Actually my needs are 1) i want to open a save dialog for storing the image (in html page) 2)the image file was in PNG format before it was converted into base64String, but your code saves the image as eg.part format.here i want the image to be stored in PNG format itself. – vengatesh Nov 01 '11 at 05:18
  • @vengatesh : This is not my code that proposes you to save file in a ***.part** format - this is how browser handles the request, since the image is not bytecoded. There is no easy way to achieve a goal. One of possible solutions is configuring a server to respond with certain headers that contain filename and extension - you should google on that matter... – John Doe Nov 01 '11 at 10:02
  • @JohnDoe - Thanks for this answer I used it to build a solution I had to a similar problem and it works perfectly! Only one thing I would like to provide a name for the file download the browser currently it defaults to 'download' (in Chrome anyway). Any ideas? – benedict_w Dec 01 '12 at 17:00
  • @benedict_w Yeah, I had the same problem. In order to let a browser know how to name a file (including extension) I used a PHP-script that modified `content-disposition` header in an HTTP-response. Hope this helps! – John Doe Dec 01 '12 at 23:28
  • Yeah I also tried this with `header('Content-Disposition: attachment; filename="my-image.png"');` but it didn't work (my image is already generated via a server side script). Did you use the technique here? http://greenethumb.com/article/1429/user-friendly-image-saving-from-the-canvas/ - I don't really want to send my bytecode back to the server if I don't have to! – benedict_w Dec 02 '12 at 08:48
  • @benedict_w I guess it was a similar logic, except for getting data from canvas (just adding the HTTP-headers). – John Doe Dec 03 '12 at 19:28
  • @Ankita, I suppose you can use the loaded image source attribute value. – John Doe Oct 16 '15 at 00:11
  • @JohnDoe can you tell me how to create data url for image, as I am showing image from database to table. So I have to add functionality to download image. Thanks – Maninder Jan 08 '16 at 20:28
  • @vengatesh you should accept this answer for the responder. Thanks guys! – Eric Hodonsky Apr 11 '17 at 18:56
  • 1
    jsfiddle is dead :( – Sabrina Luo Mar 07 '19 at 15:13
3

This Works

function saveBase64AsFile(base64, fileName) {
    var link = document.createElement("a");
    document.body.appendChild(link);
    link.setAttribute("type", "hidden");
    link.href = "data:text/plain;base64," + base64;
    link.download = fileName;
    link.click();  
    document.body.removeChild(link);
}

Based on the answer above but with some changes

0

Check out https://github.com/eligrey/FileSaver.js/ which wraps the HTML5 method and provides workarounds for e.g. IE10.

EoghanM
  • 25,161
  • 23
  • 90
  • 123