83

How can I make a browser display a "save as dialog" so the user can save the content of a string to a file on his system?

For example:

var myString = "my string with some stuff";
save_to_filesystem(myString,"myString.txt");

Resulting in something like this:

Example

Ry-
  • 218,210
  • 55
  • 464
  • 476
MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • You'll find the answer here: http://stackoverflow.com/questions/2897619/using-html5-javascript-to-generate-and-save-a-file – demux Jul 05 '12 at 00:55
  • @ArnarYngvason is this the same process as doing this in angularjs? – bleyk Jan 11 '16 at 06:41
  • 1
    @bleykFaust, the procedure is the same across all frontend js applications. – demux Jan 11 '16 at 15:22

7 Answers7

43

EDIT 2022: Please see other answers regarding File System API


In case anyone is still wondering...

I did it like this:

<a href="data:application/xml;charset=utf-8,your code here" download="filename.html">Save</a>

can't remember my source but it uses the following techniques\features:

  1. html5 download attribute
  2. data URI's

Found the reference:

http://paxcel.net/blog/savedownload-file-using-html5-javascript-the-download-attribute-2/


EDIT: As you can gather from the comments, this does NOT work in

  1. Internet Explorer (however works in Edge v13 and later)
  2. Opera Mini

http://caniuse.com/#feat=download

Craig Wayne
  • 4,499
  • 4
  • 35
  • 50
  • 2
    As of Jan 2019, it works in (non iOS) Safari, Android Browser. Still no iOS Safari, Opera Mini, or IE https://caniuse.com/#feat=download – SeanMC Jan 14 '19 at 15:12
  • 5
    @SeanKPS Can you force it to **prompt the user** for the folder in which to save it, despite browser settings? – Kyll Feb 21 '19 at 16:06
  • 1
    Well, its modern, short, and it works. But, **it gives no "save as..." dialog** anymore. I wish user be able to change file name before save. – Vaulter Nov 19 '19 at 18:50
  • 2
    In win7 chrome 79, **relies on browser setting**. If user unchecked 'Ask where to save each file before downloading', it will download directly. – user2959760 Dec 31 '19 at 10:26
  • 1
    Best method now (May 2021) seems to be the File system access API detailed here: https://web.dev/file-system-access/ – Alex Horlock May 20 '21 at 09:19
  • File system access looks pretty cool, but from what I can tell it has very spotty support: https://caniuse.com/native-filesystem-api – IanVS May 27 '21 at 04:08
28

There is a new spec called the Native File System API that allows you to do this properly like this:

const result = await window.chooseFileSystemEntries({ type: "save-file" });

There is a demo here, but I believe it is using an origin trial so it may not work in your own website unless you sign up or enable a config flag, and it obviously only works in Chrome. If you're making an Electron app this might be an option though.

Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • 10
    Tackling this in May 2021, this seemed to be the easiest method: https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker. Note the API has changed since the original post. See this helpful article as well: https://web.dev/file-system-access/ – Alex Horlock May 20 '21 at 09:16
  • 4
    This doesn't work in most browsers yet. – meles Dec 19 '21 at 16:33
17

There is a javascript library for this, see FileSaver.js on Github

However the saveAs() function won't send pure string to the browser, you need to convert it to blob:

function data2blob(data, isBase64) {
  var chars = "";

  if (isBase64)
    chars = atob(data);
  else
    chars = data;

  var bytes = new Array(chars.length);
  for (var i = 0; i < chars.length; i++) {
    bytes[i] = chars.charCodeAt(i);
  }

  var blob = new Blob([new Uint8Array(bytes)]);
  return blob;
}

and then call saveAs on the blob, as like:

var myString = "my string with some stuff";
saveAs( data2blob(myString), "myString.txt" );

Of course remember to include the above-mentioned javascript library on your webpage using <script src=FileSaver.js>

Tomas M
  • 6,919
  • 6
  • 27
  • 33
  • 17
    This just saves "myString.txt" to my Downloads folder, without showing a dialog box. – meetar Apr 17 '15 at 01:17
  • Ahh, no I don't! They all save automatically. So is this always a per-browser feature, or is there a way to trigger the behavior with a script? – meetar Apr 17 '15 at 20:33
  • 7
    I believe it is a per-browser feature. In chrome, you set it in settings, advanced settings, checkbox "Ask where to save each file before downloading" – Tomas M Apr 19 '15 at 07:26
  • 1
    It is not possible to get a Win32 SaveAs dialog under normal circumstances. Best method is to use filename from a user input. – bryc Jan 14 '17 at 16:19
10

This is 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), using nothing but JS.

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

Solution using only javascript

function saveFile(fileName,urlFile){
    let a = document.createElement("a");
    a.style = "display: none";
    document.body.appendChild(a);
    a.href = urlFile;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
    a.remove();
}

let textData = `El contenido del archivo
que sera descargado`;
let blobData = new Blob([textData], {type: "text/plain"});
let url = window.URL.createObjectURL(blobData);
//let url = "pathExample/localFile.png"; // LocalFileDownload
saveFile('archivo.txt',url);
Ronald Coarite
  • 4,460
  • 27
  • 31
5

Using showSaveFilePicker():

const handle = await showSaveFilePicker({
    suggestedName: 'name.txt',
    types: [{
        description: 'Text file',
        accept: {'text/plain': ['.txt']},
    }],
});

const blob = new Blob(['Some text']);

const writableStream = await handle.createWritable();
await writableStream.write(blob);
await writableStream.close();
Richie Bendall
  • 7,738
  • 4
  • 38
  • 58
  • 3
    Not currently supported in Firefox/Safari. – Silas Reinagel Sep 01 '22 at 16:17
  • For TypeScript, you need to doo the following changes: https://stackoverflow.com/questions/71309058/property-showsavefilepicker-does-not-exist-on-type-window-typeof-globalthis – C.Aglar Mar 24 '23 at 17:32
1

Inspired by @ronald-coarite answer, here is my solution:

function saveTxtToFile(fileName: string, textData: string) {
  const blobData = new Blob([textData], { type: 'text/plain' });
  const urlToBlob = window.URL.createObjectURL(blobData);

  const a = document.createElement('a');
  a.style.setProperty('display', 'none');
  document.body.appendChild(a);
  a.href = urlToBlob;
  a.download = fileName;
  a.click();
  window.URL.revokeObjectURL(urlToBlob);
  a.remove();
}

saveTxtToFile('myFile.json', JSON.stringify(myJson));
Hans
  • 1,162
  • 11
  • 18