31

I am being asked to make a "download" button that downloads the contents of a textarea on the same page as a file, with the browser's "Save As..." dialog showing up. Copy/paste would do the job just fine, but it is a "requirement".

Right now, I am just posting the contents of the textarea to the server, which echos them back with Content-disposition: attachment slapped on. Is there a way to do this with just client-side Javascript?

Nightfirecat
  • 11,432
  • 6
  • 35
  • 51
Thilo
  • 257,207
  • 101
  • 511
  • 656

9 Answers9

31

This may be what you are looking for: http://thiscouldbebetter.wordpress.com/2012/12/18/loading-editing-and-saving-a-text-file-in-html5-using-javascrip/

It uses the browser's download dialogue, but supports only FF and Chrome, and maybe more browsers now?


   function saveTextAsFile(textToWrite, fileNameToSaveAs)
    {
     var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'}); 
     var downloadLink = document.createElement("a");
     downloadLink.download = fileNameToSaveAs;
     downloadLink.innerHTML = "Download File";
     if (window.webkitURL != null)
     {
      // Chrome allows the link to be clicked
      // without actually adding it to the DOM.
      downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
     }
     else
     {
      // Firefox requires the link to be added to the DOM
      // before it can be clicked.
      downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
      downloadLink.onclick = destroyClickedElement;
      downloadLink.style.display = "none";
      document.body.appendChild(downloadLink);
     }
    
     downloadLink.click();
    }
<textarea id=t>Hey</textarea><br>
<button onclick=saveTextAsFile(t.value,'download.txt')>Download</button>
Smart Manoj
  • 5,230
  • 4
  • 34
  • 59
NatureShade
  • 2,187
  • 1
  • 19
  • 27
  • Whilst this may theoretically answer the question, [it would be preferable](http://meta.stackexchange.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – John Dvorak Oct 12 '13 at 09:20
  • Today it is not neccessary anymore to check for webkitURL. http://caniuse.com/#search=URL Seems also that Edge still doesnot support this feature – Wolfgang Blessen Jul 06 '17 at 07:03
  • 1
    "Error: destroyClickedElement is not defined" . copying code from the internet is not good for health. – crisc2000 Oct 07 '19 at 18:24
10

I found a simple solution here: https://codepen.io

My text area:<br />
<textarea rows='10' cols='80' id='myTextArea' ></textarea>

<br /><br />

Download button: <br />
<input value='download' type='button'
onclick='doDL(document.getElementById("myTextArea").value)' />


<script type='text/javascript'>
function doDL(s){
    function dataUrl(data) {return "data:x-application/text," + escape(data);}
    window.open(dataUrl(s));
}
</script>

Hope it will help.

Pedro404
  • 158
  • 5
Cyrlop
  • 1,894
  • 1
  • 17
  • 31
  • you are using an invalid content type; doesn't application/octet-stream have the same effect? – Julian Reschke Jul 05 '13 at 11:42
  • @CyrLop -- awesome answer you posted here.... do you know if theres a way to hardcode a file name so the save dialoge opens with a filename the user can edit, but that is for instance, the current date -- yyyymmdd.txt ? – tamak Oct 30 '15 at 16:31
  • @tamak, see my answer below based on this one. – bbarker May 15 '19 at 15:59
9

You could try window.location = "data:application/octet-stream,"+text but that doesn't provide a mechanism through which you can suggest a name, and also IE has a very small cap on the maximum length of a data URI which could be a problem.

olliej
  • 35,755
  • 9
  • 58
  • 55
7

There were some javascript libraries that did this kind of thing, via small embedded SWF file. For example this one.

yacoob
  • 811
  • 1
  • 11
  • 20
4

Absolutely possible using this cross browser JavaScript implementation of the HTML5 saveAs function: https://github.com/koffsyrup/FileSaver.js

If all you want to do is save text then the above script works in all browsers(including all versions of IE), no SWF required.

superphonic
  • 7,954
  • 6
  • 30
  • 63
4

You can use data: URIs and give it a file name, while still downloading the text. Try this:

document.getElementById("download").onclick = function(){
  var l = document.createElement("a");
  l.href = "data:text/plain;charset=UTF-8," + document.getElementById("dload-txt").value;
  l.setAttribute("download", document.getElementById("dload-fn").value);
  l.click();
}
textarea { width: 200px; height: 75px }
input { width: 200px }
<textarea placeholder="Enter text to download" id="dload-txt"></textarea><br/>
<input placeholder="Enter file name to download as" id="dload-fn"/><br/><br/>
<button id="download">Download</button>

This works in most browsers.

What it does is get the necessary data from the textarea and input, create a link that has an href to data:text/plain;UTF-8,<textarea data>, and set the download attribute with the name set by the <input> element. Then click the link, which will download the text.

The only not-all-browser-compatible things here are:

  1. data: URIs for storing the data as a link. data: URIs on CanIUse

  2. click() function to click the link. HTMLElement.click() on CanIUse

  3. download attribute to signify a download. download attribute on CanIUse

So basically:

  • Does not work in IE

  • Does not work in Opera Mini

  • Does not work in very early versions of Firefox, Chrome, Safari, Opera, and iOS Safari

Otherwise, this works in all major browsers, and doesn't need any Blob object.

Blob construction on CanIUse

Blob URLs on CanIUse

Lakshya Raj
  • 1,669
  • 3
  • 10
  • 34
3

Based on @Cyrlop's answer and https://stackoverflow.com/a/41948732/3096687, this gives a way to specify a filename:

            function doDownload(str) {
              function dataUrl(data) {
                return "data:x-application/xml;charset=utf-8," + escape(data);
              }
              var downloadLink = document.createElement("a");
              downloadLink.href = dataUrl(str);
              downloadLink.download = "foo.xml";

              document.body.appendChild(downloadLink);
              downloadLink.click();
              document.body.removeChild(downloadLink);
            }

@Superphonic's solution is likely nicer if you don't mind including more bytes in your JavaScript.

bbarker
  • 11,636
  • 9
  • 38
  • 62
1

It might be possible by creating a frame, writing contents there, then calling document.execCommand('saveas', ...) in IE and something with nsIFilePicker in Mozilla, but I believe that would require some extraordinary privileges (like being part of the browser itself).

Andrey Shchekin
  • 21,101
  • 19
  • 94
  • 162
-2

Short answer: it's not posible. You have to POST it to server, and response from server can be "Content-disposition: attachment".

firem
  • 37
  • 1
  • This answer is objectively wrong given I've personally tested and gotten one of these answers working. – schizoid04 Jun 15 '21 at 15:53
  • Not sure how this ended up in the VLQ queue - it *is* an answer (just a possibly wrong one). [Wrong answers are not very low quality.](https://meta.stackoverflow.com/questions/345023/are-blatantly-wrong-answers-very-low-quality) – EJoshuaS - Stand with Ukraine Jun 15 '21 at 16:56