Export javascript data to CSV file without server interaction
Asked Answered
F

6

80

If we were on a nodeJS server, we could write a header, set a mime type, and send it:

res.header("Content-Disposition", "attachment;filename="+name+".csv"); 
res.type("text/csv");
res.send(200, csvString);

and because of the headers, the browser will create a download for the named csv file.

When useful data is generated in a browser, one solution to getting it in a CSV file is to use ajax, upload it to the server, (perhaps optionally save it there) and get the server to send it back with these headers to become a csv download back at the browser.

However, I would like a 100% browser solution that does not involve ping-pong with the server.

So it occurred to me that one could open a new window and try to set the header with a META tag equivalent.

But this doesn't work for me in recent Chrome.

I do get a new window, and it contains the csvString, but does not act as a download.

I guess I expected to get either a download in a bottom tab or a blank new window with a download in a bottom tab.

I'm wondering if the meta tags are correct or if other tags are also needed.

Is there a way to make this work without punting it to the server?

JsFiddle for Creating a CSV in the Browser (not working - outputs window but no download)

var A = [['n','sqrt(n)']];  // initialize array of rows with header row as 1st item
for(var j=1;j<10;++j){ A.push([j, Math.sqrt(j)]) }
var csvRows = [];
for(var i=0,l=A.length; i<l; ++i){
    csvRows.push(A[i].join(','));   // unquoted CSV row
}
var csvString = csvRows.join("\n");
console.log(csvString);
var csvWin = window.open("","","");
csvWin.document.write('<meta name="content-type" content="text/csv">');
csvWin.document.write('<meta name="content-disposition" content="attachment;  filename=data.csv">  ');
csvWin.document.write(csvString);
Ferd answered 24/7, 2013 at 14:2 Comment(3)
Maybe this helps: #6797474Conaway
@Conaway Data URLs are certainly obscure enough to be a possibility. It is certainly not obvious, I haven't seen a library that does it, and another answer from 2011 says it can't be done.Ferd
similar data URL tricks are in answer by @Italo to https://mcmap.net/q/245783/-export-to-csv-in-jqueryFerd
I
165

There's always the HTML5 download attribute :

This attribute, if present, indicates that the author intends the hyperlink to be used for downloading a resource so that when the user clicks on the link they will be prompted to save it as a local file.

If the attribute has a value, the value will be used as the pre-filled file name in the Save prompt that opens when the user clicks on the link.

var A = [['n','sqrt(n)']];

for(var j=1; j<10; ++j){ 
    A.push([j, Math.sqrt(j)]);
}

var csvRows = [];

for(var i=0, l=A.length; i<l; ++i){
    csvRows.push(A[i].join(','));
}

var csvString = csvRows.join("%0A");
var a         = document.createElement('a');
a.href        = 'data:attachment/csv,' +  encodeURIComponent(csvString);
a.target      = '_blank';
a.download    = 'myFile.csv';

document.body.appendChild(a);
a.click();

FIDDLE

Tested in Chrome and Firefox, works fine in the newest versions (as of July 2013).
Works in Opera as well, but does not set the filename (as of July 2013).
Does not seem to work in IE9 (big suprise) (as of July 2013).

An overview over what browsers support the download attribute can be found Here
For non-supporting browsers, one has to set the appropriate headers on the serverside.


Apparently there is a hack for IE10 and IE11, which doesn't support the download attribute (Edge does however).

var A = [['n','sqrt(n)']];

for(var j=1; j<10; ++j){ 
    A.push([j, Math.sqrt(j)]);
}

var csvRows = [];

for(var i=0, l=A.length; i<l; ++i){
    csvRows.push(A[i].join(','));
}

var csvString = csvRows.join("%0A");

if (window.navigator.msSaveOrOpenBlob) {
    var blob = new Blob([csvString]);
    window.navigator.msSaveOrOpenBlob(blob, 'myFile.csv');
} else {
    var a         = document.createElement('a');
    a.href        = 'data:attachment/csv,' +  encodeURIComponent(csvString);
    a.target      = '_blank';
    a.download    = 'myFile.csv';
    document.body.appendChild(a);
    a.click();
}
Intisar answered 24/7, 2013 at 14:12 Comment(25)
+1, Wow. I didn't expect this kind of answer so soon. Two issues: (1) the newlines are not in the file, so it isn't really a standard csv anymore; (2) downloads as name "true".Ferd
Couldn't figure out how to set a filename, but I'm sure it's possible somehow. It's usually attachment;filename=somefile.csv but that doesn't seem to work for csv strings ?Intisar
Another way to do this would be to just url encode the string an post it to a PHP file that echo's it out with the content disposition headers set, but then it wouldn't be entirely clientside anymore, but that is the usual way of doing this.Intisar
It seems a.download can set the file name but only on chrome. #284456Ferd
The only question now is how to preserve the newlines. I'm thinking some kind of encoding of the string before appending it to a.href. Base64? url encoded?Ferd
@Ferd - stupid me, of course you put the name in the download attribute, I knew that, but set it to true for some reason, and couldn't really understand why the file was always named true.Intisar
a.href = 'data:attachment/csv,' + encodeURIComponent(csvString);Ferd
That works too, but would convert any special characters not just the newlines, but if that's not an issue, no problem.Intisar
I suspect they might get converted back in the actual file... but not sureFerd
OK, you get a check mark. This is blog-coderwall-github worthy stuff, people really don't know how to do this.Ferd
If it's decoded in the file, that's something the browser does. I tested and works fine with URI encoding in Chrome, and checking the file, it does get decoded before it's saved, strangely enough! And yes, this is not something that is commonly done, it's all very new with the download attributes and everything.Intisar
Thanks for helping me learn about these data URLs. I created a library (license: GPL) that will handle these CSV downloads, among other things. github.com/DrPaulBrewer/html5csvFerd
Still no IE for Ubuntu either ... :-)Ferd
The filename did not work for me in Chrome. It worked when I changed a.href to this: a.href = window.URL.createObjectURL(new Blob([csvString], { type: "attachment/csv" }));Chromonema
@FacioRatio Google changed something in Chrome. breaking the ability to set the filename. See: code.google.com/p/chromium/issues/detail?id=373182Ferd
hey I tried the FIDDLE. But the file being downloaded is in .part format and not .csv. I tried in Firefox 19.0.2.Schelling
Is the maximum length of a link-url (a.href) of a browser affected by this href-assignment?Offcolor
With the target attribute set to _blank, this does't work in Safari. And Safari does not support the download attribute, so the file name will not work too, it downloads 'Unknown'.Highclass
it crashes Chrome with a 20.000 rows csv download #23301967Kaif
I was able to activate the anchor without adding it to the dom. (one less line)Paramagnetic
white space in between words are removed, i.e. South Africa -> SouthAfricaDib
"There's always the HTML5 download attribute" ... no not really, not in older browsers ;)Bruiser
Doesn't work from me after EncodeURIComponent with '%0A' as a delimiter. You may need to replace it with '/r/n/'.Semeiology
the answer in the below link has the hack for IE download -> https://mcmap.net/q/247180/-download-text-csv-content-as-files-from-server-in-angular hope it helps someone.Mano
I ran into trouble with this when there are commas in the data.Admissible
S
30

@adeneo answer works for Firefox and chrome... For IE the below can be used.

if (window.navigator.msSaveOrOpenBlob) {
  var blob = new Blob([decodeURIComponent(encodeURI(result.data))], {
    type: "text/csv;charset=utf-8;"
  });
  navigator.msSaveBlob(blob, 'FileName.csv');
}
Shockproof answered 30/12, 2014 at 2:51 Comment(1)
I can confirm this works in IE11, and have updated my library html5csv to include IE 11 support based on this technique. I credited your post in the code comments. Thanks a lot.Ferd
L
15

See adeneo's answer, but don't forget encodeURIComponent!

a.href     = 'data:application/csv;charset=utf-8,' + encodeURIComponent(csvString);

Also, I needed to do "\r\n" not just "\n" for the row delimiter.

var csvString = csvRows.join("\r\n");

Revised fiddle: http://jsfiddle.net/7Q3c6/

Lefthand answered 14/7, 2014 at 3:28 Comment(0)
B
7

Once I packed JS code doing that to a tiny library:

https://github.com/AlexLibs/client-side-csv-generator

The Code, Documentation and Demo/Playground are provided on Github.

Enjoy :)

Pull requests are welcome.

Bibliotheca answered 4/1, 2015 at 0:39 Comment(0)
S
3

We can easily create and export/download the excel file with any separator (in this answer I am using the comma separator) using javascript. I am not using any external package for creating the excel file.

    var Head = [[
        'Heading 1',
        'Heading 2', 
        'Heading 3', 
        'Heading 4'
    ]];

    var row = [
       {key1:1,key2:2, key3:3, key4:4},
       {key1:2,key2:5, key3:6, key4:7},
       {key1:3,key2:2, key3:3, key4:4},
       {key1:4,key2:2, key3:3, key4:4},
       {key1:5,key2:2, key3:3, key4:4}
    ];

for (var item = 0; item < row.length; ++item) {
       Head.push([
          row[item].key1,
          row[item].key2,
          row[item].key3,
          row[item].key4
       ]);
}

var csvRows = [];
for (var cell = 0; cell < Head.length; ++cell) {
       csvRows.push(Head[cell].join(','));
}
            
var csvString = csvRows.join("\n");
let csvFile = new Blob([csvString], { type: "text/csv" });
let downloadLink = document.createElement("a");
downloadLink.download = 'MYCSVFILE.csv';
downloadLink.href = window.URL.createObjectURL(csvFile);
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
Serf answered 28/9, 2018 at 10:3 Comment(0)
B
2

See adeneo's answer, but to make this work in Excel in all countries you should add "SEP=," to the first line of the file. This will set the standard separator in Excel and will not show up in the actual document

var csvString = "SEP=, \n" + csvRows.join("\r\n");
Boak answered 15/10, 2018 at 11:59 Comment(2)
I doubt R or Pandas (Python) is going to like that in a csv file.Ferd
@Ferd That might be true, but in my case I needed to make a csv file to be opened in excel and I want it to open in a presentable manner regardless of locality. If you have a better solution for this I'd love to hear it!Boak

© 2022 - 2024 — McMap. All rights reserved.