26

I have a situation where I need to give my users the option to save some data stored locally in their client memory to disk. The current workaround I have is having a handler like this

(define-handler (download-deck) ((deck :json))
  (setf (header-out :content-type) "application/json"
    (header-out :content-disposition) "attachment")
  deck)

which does exactly what it looks like. The client sends their data up, and saves the returned file locally.

This seems stupid.

Please, please tell me there's a better, simpler, cross-browser way to let a client save some local data to their disk with a file-save dialog box.

Every answer I read on the subject either says "no, you can't save files with javascript" or "yes, there's this one semi-documented piece of the Chrome API that might let you do it in three pages".

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
Inaimathi
  • 13,853
  • 9
  • 49
  • 93
  • 1
    This may help: https://github.com/eligrey/FileSaver.js – David Morabito Sep 09 '13 at 01:46
  • Have you considered using `localStorage` instead? http://diveintohtml5.info/storage.html doesn't exactly involve *files* but you should be able to save and retrieve data as well. – icedwater Sep 09 '13 at 01:49
  • @Dave - Doesn't support Conkeror, but I guess that's asking a bit much :P. Otherwise, cross-browser, HTML5 compliant, and simple (I've already implemented it). I like it; if it were an answer, I'd have accepted it. – Inaimathi Sep 09 '13 at 02:07
  • @icedwater - sadly, files are half the point. I'm building a tabletop prototyping tool,and I want users to be able to distribute their data without involving any particular server. That does look interesting though. – Inaimathi Sep 09 '13 at 02:10
  • Ah, so the point of having the files is so that the data can be shared later on. Gotcha. – icedwater Sep 09 '13 at 02:25
  • @Inaimathi you got it =P – David Morabito Sep 09 '13 at 02:46

3 Answers3

31

This "FileSaver" library may help. If you want it to be reasonably cross-browser, you'll also need this to implement the W3C Blob API in places it's not already implemented. Both respect namespaces, and are completely framework agnostic, so don't worry about naming issues.

Once you've got those included, and as long as you're only saving text files, you should be able to

var blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
saveAs(blob, "hello world.txt");

Note that the first argument to new Blob has to be a list of strings, and that you're expected to specify the filename. As in, the user will see this file being downloaded locally, but won't be able to name it themselves. Hopefully they're using a browser that handles local filename collisions...

fscheidl
  • 2,281
  • 3
  • 19
  • 33
David Morabito
  • 1,498
  • 1
  • 12
  • 22
12

This is my code:

<a id='tfa_src_data'>Export</a>

document.getElementById('tfa_src_data').onclick = function() {                  
                        var csv = JSON.stringify(localStorage['savedCoords']);
                        var csvData = 'data:application/csv;charset=utf-8,' 
                                       + encodeURIComponent(csv);
                        this.href = csvData;
                        this.target = '_blank';
                        this.download = 'filename.txt';
                    };

You can use various data types.

tomasb
  • 1,663
  • 2
  • 22
  • 29
2

Depending on what you are trying to do exactly, the HTML5 local storage concept might help you.

So what is HTML5 Storage? Simply put, it’s a way for web pages to store named key/value pairs locally, within the client web browser. Like cookies, this data persists even after you navigate away from the web site, close your browser tab, exit your browser, or what have you. Unlike cookies, this data is never transmitted to the remote web server (unless you go out of your way to send it manually). http://diveintohtml5.info/storage.html

There's also the Filesystem API (so far only implemented in Chrome AFAIK) http://www.html5rocks.com/en/tutorials/file/filesystem/

KarlM
  • 1,614
  • 18
  • 28