How to download the current page as a file / attachment using Javascript?
Asked Answered
T

6

11

I am aware of the hidden iFrame trick as mentioned here (https://mcmap.net/q/67876/-starting-file-download-with-javascript) and in other answers.

I am interested in a similar problem:

How can I use Javascript to download the current page (IE: the current DOM, or some sub-set of it) as a file?

I have a web page which fetches results from a non-deterministic query (eg. a random sample) to display to the user. I can already, via a querystring parameter, make the page return a file instead of rendering the page. I can add a "Get file version" button (our standard approach) but the results will be different to those displayed because it is a different run of the query.

Is there any way via Javascript to download the current page as a file, or is copying to the clipboard my only option?

EDIT An option suggested by Stefan Kendall and dj_segfault is to write the result server side for later retrieval. Good idea, but unfortunately writing files server side is out of the question in this instance.

How about shudder passing the innerHTML as a post parameter to another page?

Truda answered 1/9, 2011 at 14:8 Comment(0)
T
18

You can try with the protocol data:text/attachment

Like in:

<html>
<head>
    <style>
    </style>
</head>
<body>
    <div id="hello">
        <span>world</span>
    </div>
<script>
(function(){
    document.location = 
        'data:text/attachment;,' + //here is the trick
        document.getElementById('hello').innerHTML;
            //document.documentElement.innerHTML; //To Download Entire Html Source
})();
</script>
</body>
</html>

Edit after shesek comment

Tricorn answered 1/9, 2011 at 14:26 Comment(12)
No need for an iframe, he can simply document.location= to itCapitate
@Capitate Learned something big today. Thanks!Tricorn
@Truda may be with a window.open and playing with the title, but you would get a new tabTricorn
New tab would be fine, if not and advantage for this scenario.Truda
@Tricorn The <title> of the page doesn't effect the download filename (in Chrome and Firefox at least). There's no way to set the download filename... yet. The new HTML5 download attribute of <a> elements allows you to set a filename, but AFAIK this isn't supported on any browser other than Chrome dev 14.0.835.15+. When support for this is added, you can do that by creating an <a> element, pointing it at the data:... uri, setting the download attribute and emulating a click on it.Capitate
@shesek, I saw that while testing it... but the future looks bright.Tricorn
Thanks all for the help! Wait for HTML 5 yawn I guess :)Truda
If you want to download entire page Html as string Click Here.. #817718Calabria
This answer is so much win. However, it seems to truncate the output after ~3000 chars (Not to mention causes the browser to hang for a solid minute or so). Any idea why this might be the case?Airman
Update to my last comment: Trying it in Chrome worked a charm. It might be because I have 283,291,593 tabs open in Firefox and FF was just running out of memory.Airman
@Tricorn : Is there a better mime type than text/attachment? Currently, the result isn't downloaded as html.Gannet
Not allowed to navigate top frame to data URL:Pietism
A
6

To add to Mic's terrific answer above, some additional points:

  • If you have Unicode content (Or want to preserve indentation in the source), you need to convert the string to Base64 and tell the Data URI to treat the data as such:
  • (function(){
        document.location = 
            'data:text/attachment;base64,' + // Notice the new "base64" bit!
            utf8_to_b64(document.getElementById('hello').innerHTML);
                //utf8_to_b64(document.documentElement.innerHTML); //To Download Entire Html Source
    })();
    
    function utf8_to_b64( str ) {
      return window.btoa(unescape(encodeURIComponent( str )));
    }
    

    utf_to_b64() via MDN -- works in Chrome/FF.

  • You can drop this all into an anchor tag, allowing you to set the download attribute:
  • <a onclick="$(this).attr('href', 'data:text/plain;base64,' + utf8_to_b64($('html').clone().find('#generate').remove().end()[0].outerHTML));" download="index.html" id="generate">Generate static</a>
    

    This will download the current page's HTML as index.html and removes the link used to generate the output. This assumes the utf8_to_b64() function from above is defined somewhere else.

    Some useful links on Data URIs:

    Airman answered 26/6, 2013 at 11:51 Comment(0)
    C
    2

    Depending on the size and if support is needed for ancient browsers, but you can consider creating a dynamic file using data: URIs and link to it. I'be seen several places that do that. To get the brorwser to download rather than display it, play around with the content type you put in the URI and use the new html5 download attribute. (Sorry for any typos, I'm writing from my phone)

    Capitate answered 1/9, 2011 at 14:25 Comment(1)
    12 years after, an implementation of this is in my answer below :-)Germanism
    I
    1

    I don't think you're going to be able to do it exactly the way you want to. JavaScript can't create a file and download it for security reasons. Nor can it create it on the server for download.

    What I would do if I were you is, on the server side, create an output file with the session ID in the name in a temp directory as you create the output for the web page, and have a button on the web page with a link to that file.

    You'll probably want a separate process to remove files over a day old or something like that.

    Invalidate answered 1/9, 2011 at 14:17 Comment(1)
    This is what I feared. Writing server side files is, unfortunately, out of the question.Truda
    H
    0

    Can you not cache the query results, and store it by some key? That way you can reference the same report output forever, or until your file garbage collector comes along. This also implies that you can create static URLs to report outputs, which tends to be nice.

    Hypnotism answered 1/9, 2011 at 14:19 Comment(1)
    Writing server side files is, unfortunately, out of the question.Truda
    G
    0

    This is an old question, but I had to deal with the same issue in 2023, and the solution here had two problems:

    1. The downloaded file was truncated.
    2. Inability to control the name and extension of the downloaded file.

    As noted in some comments above, we now have the download attribute for a elements in html, which allows for specifying the filename. I first tried something like the following (Assuming the original filename is index.html; But read later so see the drawback of this solution):

    html:

    <a id="save" href="index.html" download="default_download_name.html">save</a>
    

    css:

    #save {display: none;}
    

    js:

    function save(){
      document.getElementById("save").click()
    }
     
    

    The problem with this solution is that it saves the original document, so if it includes any dynamic content, it will not be stored (and that was a requirement by the OP). To overcome this issue, one can do the (slightly less elegant, IMHO) following, still leveraging the download attribute, but mixing it with dynamically created data to download:

    function save() {
      const data = "<!DOCTYPE html>\n"+document.documentElement.outerHTML; // See sources below for why this gets the entire page content.
      const link = document.createElement('a');
      link.setAttribute('download', 'default_download_name.html');
      link.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data));
      link.click(); // In my tests, there was no need to add the element to the document for this to work.
    }
    

    Sources:

    • For getting the document content - here
    • For triggering a download with dynamic data: here
    Germanism answered 23/10, 2023 at 7:23 Comment(0)

    © 2022 - 2024 — McMap. All rights reserved.