How to export JavaScript array info to csv (on client side)?
Asked Answered
S

31

792

I know there are lot of questions of this nature but I need to do this using JavaScript. I am using Dojo 1.8 and have all the attribute info in array, which looks like this:

[["name1", "city_name1", ...]["name2", "city_name2", ...]]

Any idea how I can export this to CSV on the client side?

Shellans answered 19/2, 2013 at 16:49 Comment(0)
B
1292

You can do this in native JavaScript. You'll have to parse your data into correct CSV format as so (assuming you are using an array of arrays for your data as you have described in the question):

const rows = [
    ["name1", "city1", "some other info"],
    ["name2", "city2", "more info"]
];

let csvContent = "data:text/csv;charset=utf-8,";

rows.forEach(function(rowArray) {
    let row = rowArray.join(",");
    csvContent += row + "\r\n";
});

or the shorter way (using arrow functions):

const rows = [
    ["name1", "city1", "some other info"],
    ["name2", "city2", "more info"]
];

let csvContent = "data:text/csv;charset=utf-8," 
    + rows.map(e => e.join(",")).join("\n");

Then you can use JavaScript's window.open and encodeURI functions to download the CSV file like so:

var encodedUri = encodeURI(csvContent);
window.open(encodedUri);

Edit:

If you want to give your file a specific name, you have to do things a little differently since this is not supported accessing a data URI using the window.open method. In order to achieve this, you can create a hidden <a> DOM node and set its download attribute as follows:
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "my_data.csv");
document.body.appendChild(link); // Required for FF

link.click(); // This will download the data file named "my_data.csv".
Biramous answered 19/2, 2013 at 19:51 Comment(41)
this is awesome. The only problem that I am facing is that the file that is being downloaded, has a weird name and extension as .part and not .csv. How can I correct that?Shellans
From what I know, there isn't a way to do that using window.open. However, you can create a hidden link that has a download attribute set to the file name you desire. Then "clicking" this link will download the file in the name you desire, I will add it to my answer.Biramous
thanks also why am I getting extension as .part and not .csv?Shellans
If you follow what I just added to my answer you will no longer have the ".part" extension. Unless of course your download attribute has the ".part" extension :)Biramous
try writing it into a blob and then getting a blob url.Yardarm
@Markasoftware, creating a Blob object is much more straightforward, however, the support for using the Blob constructor/URLs just got added to IE10, so there would be a problem using this code if you are trying to support IE. Of course, the same is true about using the download attribute (I don't think IE supports that at all yet...)Biramous
i have no clue about blobs. Can u give me a quick link to get some more details about it?Shellans
@Default, Im using your solution. The file is always .part :s Im using FF. What are my alternatives?Worm
@skdnewbie u cannot use this solution on any other browser except ChromeShellans
good to know. Looks like the recent upgrade supports the featureShellans
I'm unable to make the download work on FF 24 on OSX. It works in Chrome which is awesome.Motmot
Hi! Do you have some idea of how to make this work with non-ASCII tabs like spanish, hebrew, arabic etc languagesClick
I had to add document.body.appendChild(link); to get full support in FF.Allround
@ClemensTolboom had the same problem, but Hardbyte 's answer did the trick.Turgot
This might be a longshot to ask, but does anyone know how to save the csv file in an encoding other than utf-8? I want to use Japanese (shift-jis) but this doesn't seem to work: ";charset=shift-jis"Deuce
Adding a first line sep=, would make the data render in different columns upon opening the file in Excel as well.Disembogue
If I use a semicolon as the delimiter instead of a comma, will it still work?Rochet
Is there any limit on the length of the URL being navigated to with "window.open"?Shifflett
This answer is wrong: it will fail for the case data = [["Hello, world"]]. That will output two columns when it should output one.Luxor
This works fine for like ~7000 rows. But starts giving this error : NETWORK_INVALID_REQUEST. Is any body else facing this issue too? Is there any upper limit of data on encodeURIComponent() function or something? I am using Chrome as the browser.Tallula
Since Chrome 53 script generated events will not invoke the default action. developer.mozilla.org/en/docs/Web/API/Event/isTrustedGoshawk
Nothing wrong with adding a newline at the end of the file.Clipclop
@Tallula I have same problem. Can't figure out how to download large csv.Clynes
@Tallula The answer is you have to use a blob type for something that large then it'll work fine, e.g: blob = new Blob([csvContent], {type: "text/csv"}); href = window.URL.createObjectURL(blob); More details: https://mcmap.net/q/55248/-javascript-blob-filename-without-linkAwait
what will happen, if the array contains some data with comma.in this case,I am thinking it will failTorsion
I just testd and did'nt work in IE11, the error is the following: The code on this page disabled back and forward caching. For more information, see: go.microsoft.com/fwlink/?LinkID=291337 any clue to get working in IE11?Renunciation
Fixed as per @WalterTross's commentsShanelleshaner
tried this and it works however, some characters like Ñ, ñ does not appear correctly in excel but works fine in libre calcArianism
My CSV is 12874767 characters, so i did it slightly differently: var csv = jsonArrayToCsv(_filteredForExport); var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); var blobUrl = URL.createObjectURL(blob); window.open(blobUrl); OR just output blogUrl to console and click on it.Sagamore
This is simply not working. Look at the answer of Monu for a currently working alternative!Whenas
How to save csv file in folder and how to store data in column instead of rowTantalum
I am looking for a solution where the user can fill in the filename. With this solution a file with the name "download" is automatically downloaded in Chrome.Sankaran
If you want this file to be properly displayed in Excel, prepend the file with + "\ufeff"This tells excel it is UTF8 encodedCarinthia
It appears window.open() is blocked by adblockers. Is there any way around this?Transit
If you want to support Excel to read UTF-8 characters yo need to add a BOM at the begining of file. "\ufeff"+csvContentDebut
what is solution if data is couple of Mb, and href att is cutting these data?Cajole
This answer currently has multiple issues including: It fails if the data contains a comma. It fails if the data contains a line-break. It (kind of) fails if the data contains a quotation mark. It does not include a so-called BOM (which you might or might not want depending on the usecase).Clarissa
Why \r is used in first code snippet?Sateen
@MuhammadBilal Windows uses \r\n whereas Linux only uses \n.Birdwell
Please notice window.open('data:...') was no longer working after Chromium 61 groups.google.com/a/chromium.org/g/blink-dev/c/GbVcuwg_QjM/m/…Tetrafluoroethylene
This approach breaks if the csv contains a hash #Perfecto
N
361

Based on the answers above I created this function that I have tested on IE 11, Chrome 36 and Firefox 29

function exportToCsv(filename, rows) {
    var processRow = function (row) {
        var finalVal = '';
        for (var j = 0; j < row.length; j++) {
            var innerValue = row[j] === null ? '' : row[j].toString();
            if (row[j] instanceof Date) {
                innerValue = row[j].toLocaleString();
            };
            var result = innerValue.replace(/"/g, '""');
            if (result.search(/("|,|\n)/g) >= 0)
                result = '"' + result + '"';
            if (j > 0)
                finalVal += ',';
            finalVal += result;
        }
        return finalVal + '\n';
    };

    var csvFile = '';
    for (var i = 0; i < rows.length; i++) {
        csvFile += processRow(rows[i]);
    }

    var blob = new Blob([csvFile], { type: 'text/csv;charset=utf-8;' });
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    } else {
        var link = document.createElement("a");
        if (link.download !== undefined) { // feature detection
            // Browsers that support HTML5 download attribute
            var url = URL.createObjectURL(blob);
            link.setAttribute("href", url);
            link.setAttribute("download", filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

For example: https://jsfiddle.net/jossef/m3rrLzk0/

Nitroglycerin answered 23/7, 2014 at 23:32 Comment(17)
Could fall-back to window.open in an else of link.download !== undefined.Kickstand
In Safari link.download and setting link.style do not work. It works when juggling a few lines around restrict the link.download checking by these lines: if (link.download !== undefined) { link.setAttribute("download", filename);} else { link.setAttribute("target", "_blank");} link.setAttribute("style", "visibility:hidden");Lassiter
This is a nice piece of code. Any chance you'd be willing to license this under something more liberal than the SO default of CC-BY-SA? For example, CC0, MIT, BSD, Apache, X11 . . . meta.stackexchange.com/questions/12527/…Midnight
I have been using this method to implement Excel export in quite some web applications. But Chrome 43+ now has moved DOM attributes to the prototype chain. An exception is thrown at link.style.visibility='hidden'. B/c the DOM attribute is readonly. More details can be find in updates.html5rocks.com/2015/04/… under the section "Writing to read-only properties in strict mode will throw an error"Karaganda
On Chrome you don't need to append the link to the documentIshmaelite
for UTF-8 non-ascii characters, I needed to prepend '\ufeff' to csvFile (see #17879698)Era
@XavierJohn hey how to you pass an array to the function?Beebe
You should set the separator comma in order to properly open in Excel 2010 and above: let csvFile = 'sep=,' + '\n';Tokharian
is it posible to have a cell text in bold?Renunciation
Line 5 fails if row[j] is undefined for whatever reason. var innerValue = (row[j] === null || row[j] === undefined) ? '' : row[j].toString(); worked for me.Malm
It is definitely perfect solution and that is why at first place - for downloading csv with big data, otherwise it will throw Error on download > X data bytes (depending on browser/version) - tested.Mencher
how can i use image with this scriptUnquiet
This response is the best one so far. It includes cases with special characters and parentheses.Ruzich
I used the download section of this answer, and it worked well on Chrome, thanks!Languor
Unlike the current accepted answer, this does not get blocked by adblockers. Great!Transit
As of now, this works beautifully in all major browsers including Safari on MacBordelon
my favorite bit var result = innerValue.replace(/"/g, '""'); if (result.search(/("|,|\n)/g) >= 0) result = '"' + result + '"';Pagas
A
141

A minimalistic yet feature-complete solution :)

/** Convert a 2D array into a CSV string
 */
function arrayToCsv(data){
  return data.map(row =>
    row
    .map(String)  // convert every value to String
    .map(v => v.replaceAll('"', '""'))  // escape double quotes
    .map(v => `"${v}"`)  // quote it
    .join(',')  // comma-separated
  ).join('\r\n');  // rows starting on new lines
}

Example:

let csv = arrayToCsv([
  [1, '2', '"3"'],
  [true, null, undefined],
]);

Result:

"1","2","""3"""
"true","null","undefined"

Now download it as a file:


/** Download contents as a file
 * Source: https://mcmap.net/q/53850/-how-to-export-javascript-array-info-to-csv-on-client-side
 */
function downloadBlob(content, filename, contentType) {
  // Create a blob
  var blob = new Blob([content], { type: contentType });
  var url = URL.createObjectURL(blob);

  // Create a link to download it
  var pom = document.createElement('a');
  pom.href = url;
  pom.setAttribute('download', filename);
  pom.click();
}

Download it:

downloadBlob(csv, 'export.csv', 'text/csv;charset=utf-8;')

save dialog

resulting file

Allusion answered 26/6, 2021 at 21:35 Comment(4)
This is late but really the best answer here.Playwriting
Community should take example in this A :-) , Separator, Enclosure, Escape(character) are generic terms one could use to store the formatting characters. (here " is both escape and enclosure - it's not always like that and you have to transfer this info to CSV receivers)Iqbal
I would remove the created anchor element and the object url. See URL: revokeObjectURL(): developer.mozilla.org/en-US/docs/Web/API/URL/… and document.body.removeChild(pom)Lachellelaches
Best answer here but would love to see this improved to only add quotes if the cell content has the join character in it.Fretted
L
95

This solution should work with Internet Explorer 10+, Edge, old and new versions of Chrome, FireFox, Safari, ++

The accepted answer won't work with IE and Safari.

// Example data given in question text
var data = [
  ['name1', 'city1', 'some other info'],
  ['name2', 'city2', 'more info']
];

// Building the CSV from the Data two-dimensional array
// Each column is separated by ";" and new line "\n" for next row
var csvContent = '';
data.forEach(function(infoArray, index) {
  dataString = infoArray.join(';');
  csvContent += index < data.length ? dataString + '\n' : dataString;
});

// The download function takes a CSV string, the filename and mimeType as parameters
// Scroll/look down at the bottom of this snippet to see how download is called
var download = function(content, fileName, mimeType) {
  var a = document.createElement('a');
  mimeType = mimeType || 'application/octet-stream';

  if (navigator.msSaveBlob) { // IE10
    navigator.msSaveBlob(new Blob([content], {
      type: mimeType
    }), fileName);
  } else if (URL && 'download' in a) { //html5 A[download]
    a.href = URL.createObjectURL(new Blob([content], {
      type: mimeType
    }));
    a.setAttribute('download', fileName);
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  } else {
    location.href = 'data:application/octet-stream,' + encodeURIComponent(content); // only this mime type is supported
  }
}

download(csvContent, 'dowload.csv', 'text/csv;encoding:utf-8');

Running the code snippet will download the mock data as csv

Credits to dandavis https://mcmap.net/q/55250/-javascript-set-filename-to-be-downloaded

Lepidosiren answered 27/3, 2015 at 15:29 Comment(9)
(At the very least, the HTML5 code) works without the setTimeout.Amblyoscope
@Amblyoscope cool then I'll remove the setTimeout from the example code :)Lepidosiren
Works in the latest Chrome, IE and Firefox. Thanks!Applegate
The only truly cross browser solution out here. Note it works on Safari 10.10 and mobile Safari. However, the iframe section can be replaced by just location.href = ...Electrolyze
NOTE: There is a typo in the function, it is actually URL.createObjectURL (ends with URL not Url).Ginni
Downloads csv in Safari 10.0.3, but it names it "Unknown"Actress
@Santosh that's FAR above and beyond what this does; xlsx is a zipped XML style document, not "comma separated values" (or in this case, semicolon separated values). Check out github.com/egeriis/zipcelx or similar.Himyarite
Thank you, it worked very well on one of my projects.Frederickson
Working perfect ;)Brentbrenton
S
44

People are trying to create their own csv string, which fail on edge cases, e.g. special characters, surely this is a solved problem right?

papaparse - use for JSON to CSV encoding. Papa.unparse().

import Papa from "papaparse";

const downloadCSV = (args) => {  

  let filename = args.filename || 'export.csv';
  let columns = args.columns || null;

  let csv = Papa.unparse({ data: args.data, fields: columns})
  if (csv == null) return;

  var blob = new Blob([csv]);
  if (window.navigator.msSaveOrOpenBlob)  // IE hack; see http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
      window.navigator.msSaveBlob(blob, args.filename);
  else
  {
      var a = window.document.createElement("a");
      a.href = window.URL.createObjectURL(blob, {type: "text/plain"});
      a.download = filename;
      document.body.appendChild(a);
      a.click();  // IE: "Access is denied"; see: https://connect.microsoft.com/IE/feedback/details/797361/ie-10-treats-blob-url-as-cross-origin-and-denies-access
      document.body.removeChild(a);
  }

}

Example usage

downloadCSV({ 
  filename: "filename.csv",
  data: [{"a": "1", "b": "2"}],
  columns: ["a","b"]
});

https://github.com/mholt/PapaParse/issues/175 - See this comment for browser support discussion.

Systematism answered 3/5, 2019 at 2:4 Comment(3)
I added a simple answer that uses Papa Parse as well as FileSaver.js for the download part. Feel free to update or copy if you think it's a better approach.Exercise
Do you have an example of special characters that break the simple approach of replacing " with ""? Parsing the CSV is tricky, and should handle line breaks inside quotes, commas in quotes, etc. But generating the CSV is pretty straightforwardPlaywriting
PapaParse has a lot of settings for stuff like special chars/escape chars and such. I would say this is the correct solution for most use cases since CSV is a notoriously fiddly format with many gotchas which someone who is naively searching may/may not be aware of. The quick native JS solutions are cool but shouldn't be the top answers or the recommended solution.Curvaceous
F
43

In Chrome 35 update, download attribute behavior was changed.

https://code.google.com/p/chromium/issues/detail?id=373182

to work this in chrome, use this

var pom = document.createElement('a');
var csvContent=csv; //here we load our csv data 
var blob = new Blob([csvContent],{type: 'text/csv;charset=utf-8;'});
var url = URL.createObjectURL(blob);
pom.href = url;
pom.setAttribute('download', 'foo.csv');
pom.click();
Facia answered 28/5, 2014 at 21:50 Comment(2)
You also can check this one : github.com/mholt/PapaParse/issues/175#issuecomment-201308792Aguedaaguero
Works perfectly!Hazelhazelnut
F
39

I came here looking for a bit more RFC 4180 compliance and I failed to find an implementation, so I made a (possibly inefficient) one for my own needs. I thought I would share it with everyone.

var content = [['1st title', '2nd title', '3rd title', 'another title'], ['a a a', 'bb\nb', 'cc,c', 'dd"d'], ['www', 'xxx', 'yyy', 'zzz']];

var finalVal = '';

for (var i = 0; i < content.length; i++) {
    var value = content[i];

    for (var j = 0; j < value.length; j++) {
        var innerValue =  value[j]===null?'':value[j].toString();
        var result = innerValue.replace(/"/g, '""');
        if (result.search(/("|,|\n)/g) >= 0)
            result = '"' + result + '"';
        if (j > 0)
            finalVal += ',';
        finalVal += result;
    }

    finalVal += '\n';
}

console.log(finalVal);

var download = document.getElementById('download');
download.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(finalVal));
download.setAttribute('download', 'test.csv');

Hopefully this will help someone out in the future. This combines both the encoding of the CSV along with the ability to download the file. In my example on jsfiddle. You can download the file (assuming HTML 5 browser) or view the output in the console.

UPDATE:

Chrome now appears to have lost the ability to name the file. I'm not sure what's happened or how to fix it, but whenever I use this code (including the jsfiddle), the downloaded file is now named download.csv.

Forebear answered 16/12, 2013 at 23:38 Comment(3)
Good catch Chris, I didn't test it with numeric data :)Forebear
I don't know if the last null check is necessarily expected behavior. Null is very different than an empty string. If one was to implement this, I would recommend a custom null value (eg: '[[NULL]]'). An exception for undefined may be desired as well, but I would recommend not replacing null with an empty string.Forebear
I've testing and found that you are correct. This seems to work in Chrome and Opera. Safari just opens a page with the content. Internet Explorer... well it's IE. For my situation, I'm going to generate my CSV server side and serve it that way, sadly.Forebear
G
36

The solution from @Default works perfect on Chrome (thanks a lot for that!) but I had a problem with IE.

Here's a solution (works on IE10):

var csvContent=data; //here we load our csv data 
var blob = new Blob([csvContent],{
    type: "text/csv;charset=utf-8;"
});

navigator.msSaveBlob(blob, "filename.csv")
Griner answered 8/11, 2013 at 10:47 Comment(2)
doesnt work with chrome. the prefix 'ms' makes that pretty clear even before testing :) hopefully there is something like that for webkitAstral
msSaveBlob - deprecated featureWhodunit
F
25

Working for all languages

        function convertToCsv(fName, rows) {
        var csv = '';
        for (var i = 0; i < rows.length; i++) {
            var row = rows[i];
            for (var j = 0; j < row.length; j++) {
                var val = row[j] === null ? '' : row[j].toString();
                val = val.replace(/\t/gi, " ");
                if (j > 0)
                    csv += '\t';
                csv += val;
            }
            csv += '\n';
        }

        // for UTF-16
        var cCode, bArr = [];
        bArr.push(255, 254);
        for (var i = 0; i < csv.length; ++i) {
            cCode = csv.charCodeAt(i);
            bArr.push(cCode & 0xff);
            bArr.push(cCode / 256 >>> 0);
        }

        var blob = new Blob([new Uint8Array(bArr)], { type: 'text/csv;charset=UTF-16LE;' });
        if (navigator.msSaveBlob) {
            navigator.msSaveBlob(blob, fName);
        } else {
            var link = document.createElement("a");
            if (link.download !== undefined) {
                var url = window.URL.createObjectURL(blob);
                link.setAttribute("href", url);
                link.setAttribute("download", fName);
                link.style.visibility = 'hidden';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                window.URL.revokeObjectURL(url);
            }
        }
    }



    convertToCsv('download.csv', [
        ['Order', 'Language'],
        ['1', 'English'],
        ['2', 'Español'],
        ['3', 'Français'],
        ['4', 'Português'],
        ['5', 'čeština'],
        ['6', 'Slovenščina'],
        ['7', 'Tiếng Việt'],
        ['8', 'Türkçe'],
        ['9', 'Norsk bokmål'],
        ['10', 'Ελληνικά'],
        ['11', 'беларускі'],
        ['12', 'русский'],
        ['13', 'Українська'],
        ['14', 'հայերեն'],
        ['15', 'עִברִית'],
        ['16', 'اردو'],
        ['17', 'नेपाली'],
        ['18', 'हिंदी'],
        ['19', 'ไทย'],
        ['20', 'ქართული'],
        ['21', '中国'],
        ['22', '한국어'],
        ['23', '日本語'],
    ])
Frondescence answered 20/4, 2018 at 23:7 Comment(4)
can you please help me understand what is that UTF-16 code block and what is used for here ?Beem
Hi Mar1009. This is required for some languages. For example, the Cyrillic alphabet.Frondescence
That window.URL.revokeObjectURL(url); will cause a Network Error if the downloaded data is a bit larger. Wrapping it in setTimeout() helps, see here.Darsey
For Excel 365 Apps for business I had to remove BOM marker bArr.push(255, 254) because Excel did not recognize columns. Without BOM both unicodeness and columns are recognized OK. I wonder how other versions behave.Pavlodar
I
20

You can use the below piece of code to export array to CSV file using Javascript.

This handles special characters part as well

var arrayContent = [["Séjour 1, é,í,ú,ü,ű"],["Séjour 2, é,í,ú,ü,ű"]];
var csvContent = arrayContent.join("\n");
var link = window.document.createElement("a");
link.setAttribute("href", "data:text/csv;charset=utf-8,%EF%BB%BF" + encodeURI(csvContent));
link.setAttribute("download", "upload_data.csv");
link.click(); 

Here is the link to working jsfiddle

Immersionism answered 19/9, 2018 at 21:28 Comment(1)
This should be marked as the correct answer: short, to the point, no special cases. Only modifications I did were to replace internal CR/LF with spaces.Alviani
E
20

Old question with many good answers, but here is another simple option that relies on two popular libraries to get it done. Some answers mention Papa Parse but roll their own solution for the download part. Combining Papa Parse and FileSaver.js, you can try the following:

const dataString = Papa.unparse(data, config);
const blob = new Blob([dataString], { type: 'text/csv;charset=utf-8' });
FileSaver.saveAs(blob, 'myfile.csv');

The config options for unparse are described here.

Exercise answered 4/9, 2020 at 16:35 Comment(2)
The shortest (less error-prone) and maybe most robust cross-browser approach where functionality is based on maintained libs at this point as I see it. thanks.Tinker
or just saveAs(blob, 'myfile.csv') if you're not using ES6 modules and just importing the FileSaver.js script :)Hither
S
15

There you go :

<!doctype html>  
<html>  
<head></head>  
<body>
<a href='#' onclick='downloadCSV({ filename: "stock-data.csv" });'>Download CSV</a>

<script type="text/javascript">  
    var stockData = [
        {
            Symbol: "AAPL",
            Company: "Apple Inc.",
            Price: "132.54"
        },
        {
            Symbol: "INTC",
            Company: "Intel Corporation",
            Price: "33.45"
        },
        {
            Symbol: "GOOG",
            Company: "Google Inc",
            Price: "554.52"
        },
    ];

    function convertArrayOfObjectsToCSV(args) {
        var result, ctr, keys, columnDelimiter, lineDelimiter, data;

        data = args.data || null;
        if (data == null || !data.length) {
            return null;
        }

        columnDelimiter = args.columnDelimiter || ',';
        lineDelimiter = args.lineDelimiter || '\n';

        keys = Object.keys(data[0]);

        result = '';
        result += keys.join(columnDelimiter);
        result += lineDelimiter;

        data.forEach(function(item) {
            ctr = 0;
            keys.forEach(function(key) {
                if (ctr > 0) result += columnDelimiter;

                result += item[key];
                ctr++;
            });
            result += lineDelimiter;
        });

        return result;
    }

    window.downloadCSV = function(args) {
        var data, filename, link;

        var csv = convertArrayOfObjectsToCSV({
            data: stockData
        });
        if (csv == null) return;

        filename = args.filename || 'export.csv';

        if (!csv.match(/^data:text\/csv/i)) {
            csv = 'data:text/csv;charset=utf-8,' + csv;
        }
        data = encodeURI(csv);

        link = document.createElement('a');
        link.setAttribute('href', data);
        link.setAttribute('download', filename);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
       }
</script>  
</body>  
</html>  
Stayathome answered 2/12, 2015 at 13:21 Comment(4)
Awesome answer. I'm upvoting this one as the accepted answer for some reason puts everything into a single column. This breaks it all up into separate columns and the JSON like data format support is incredibly useful.Gilstrap
This works when the link is first added to document body and then click is called. And then it is removed from dom.Permeance
Good answer, only drawback is that it doesn't work properly when the data has a column delimiter " , " i.e Address: '10 Infinite loop lane, Room 56', notice the comma after lane. I suggest you use PapaParse link to convert the data to CSV then use the above downloadCSV method for the Actual file downloadMesolithic
This works perfect for me. Just have one problem, I have some numbers in the array like '000002342' but when exported to csv, the leading zeroes get removed. Is there any way to prevent this?Doorway
L
8
//It work in Chrome and IE ... I reviewed and readed a lot of answer. then i used it and tested in both ... 

var link = document.createElement("a");

if (link.download !== undefined) { // feature detection
    // Browsers that support HTML5 download attribute
    var blob = new Blob([CSV], { type: 'text/csv;charset=utf-8;' });
    var url = URL.createObjectURL(blob);            
    link.setAttribute("href", url);
    link.setAttribute("download", fileName);
    link.style = "visibility:hidden";
}

if (navigator.msSaveBlob) { // IE 10+
   link.addEventListener("click", function (event) {
     var blob = new Blob([CSV], {
       "type": "text/csv;charset=utf-8;"
     });
   navigator.msSaveBlob(blob, fileName);
  }, false);
}

document.body.appendChild(link);
link.click();
document.body.removeChild(link);

//Regards
Lunisolar answered 13/6, 2014 at 20:34 Comment(0)
P
8

One arrow function with ES6 :

const dataToCsvURI = (data) => encodeURI(
`data:text/csv;charset=utf-8,${data.map((row, index) =>  row.join(',')).join(`\n`)}`
);

Then :

window.open(
  dataToCsvURI(
   [["name1", "city_name1"/*, ...*/], ["name2", "city_name2"/*, ...*/]]
  )
);

In case anyone needs this for , react-csv is there for that

Psittacine answered 25/11, 2016 at 10:15 Comment(2)
The react-csv library works like a charm. Great solution for anyone using modules.Farce
This fails to observe the case in which there are fields inside the CSV file with commas inside.Ga
P
8

The following is a native js solution.

function export2csv() {
  let data = "";
  const tableData = [];
  const rows = [
    ['111', '222', '333'],
    ['aaa', 'bbb', 'ccc'],
    ['AAA', 'BBB', 'CCC']
  ];
  for (const row of rows) {
    const rowData = [];
    for (const column of row) {
      rowData.push(column);
    }
    tableData.push(rowData.join(","));
  }
  data += tableData.join("\n");
  const a = document.createElement("a");
  a.href = URL.createObjectURL(new Blob([data], { type: "text/csv" }));
  a.setAttribute("download", "data.csv");
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}
<button onclick="export2csv()">Export array to csv file</button>
Parament answered 24/2, 2020 at 9:29 Comment(0)
H
7

A lot of roll-your-own solutions here for converting data to CSV, but just about all of them will have various caveats in terms of the type of data they will correctly format without tripping up Excel or the likes.

Why not use something proven: Papa Parse

Papa.unparse(data[, config])

Then just combine this with one of the local download solutions here eg. the one by @ArneHB looks good.

Hornswoggle answered 20/10, 2017 at 11:3 Comment(0)
M
6

There are two questions here:

  1. How to convert an array to csv string
  2. How to save that string to a file

All the answers to the first question (except the one by Milimetric) here seem like an overkill. And the one by Milimetric does not cover altrenative requirements, like surrounding strings with quotes or converting arrays of objects.

Here are my takes on this:

For a simple csv one map() and a join() are enough:

    var test_array = [["name1", 2, 3], ["name2", 4, 5], ["name3", 6, 7], ["name4", 8, 9], ["name5", 10, 11]];
    var csv = test_array.map(function(d){
        return d.join();
    }).join('\n');

    /* Results in 
    name1,2,3
    name2,4,5
    name3,6,7
    name4,8,9
    name5,10,11

This method also allows you to specify column separator other than a comma in the inner join. for example a tab: d.join('\t')

On the other hand if you want to do it properly and enclose strings in quotes "", then you can use some JSON magic:

var csv = test_array.map(function(d){
       return JSON.stringify(d);
    })
    .join('\n') 
    .replace(/(^\[)|(\]$)/mg, ''); // remove opening [ and closing ]
                                   // brackets from each line 

/* would produce
"name1",2,3
"name2",4,5
"name3",6,7
"name4",8,9
"name5",10,11

if you have array of objects like :

var data = [
  {"title": "Book title 1", "author": "Name1 Surname1"},
  {"title": "Book title 2", "author": "Name2 Surname2"},
  {"title": "Book title 3", "author": "Name3 Surname3"},
  {"title": "Book title 4", "author": "Name4 Surname4"}
];

// use
var csv = data.map(function(d){
        return JSON.stringify(Object.values(d));
    })
    .join('\n') 
    .replace(/(^\[)|(\]$)/mg, '');
Misapprehend answered 8/4, 2016 at 16:16 Comment(4)
If I am not mistaken I believe the .replace should specify curly vs. square brackets.Digestion
.replace is done on a string returned by values() which takes an object and returns an array of valuesMisapprehend
The values() method wasn't found when I tried your code.Digestion
Thanks! In Chrome it works without calling values() explicitly on Object. I corrected the example.Misapprehend
J
5

Create a blob with the csv data .ie var blob = new Blob([data], type:"text/csv");

If the browser supports saving of blobs i.e if window.navigator.mSaveOrOpenBlob)===true, then save the csv data using: window.navigator.msSaveBlob(blob, 'filename.csv')

If the browser doesn't support saving and opening of blobs, then save csv data as:

var downloadLink = document.createElement('<a></a>');
downloadLink.attr('href', window.URL.createObjectURL(blob));
downloadLink.attr('download', filename);
downloadLink.attr('target', '_blank');
document.body.append(downloadLink);

Full Code snippet:

var filename = 'data_'+(new Date()).getTime()+'.csv';
var charset = "utf-8";
var blob = new Blob([data], {
     type: "text/csv;charset="+ charset + ";"
});
if (window.navigator.msSaveOrOpenBlob) {
     window.navigator.msSaveBlob(blob, filename);
} else {
    var downloadLink = document.element('<a></a>');
    downloadLink.attr('href', window.URL.createObjectURL(blob));
    downloadLink.attr('download', filename);
    downloadLink.attr('target', '_blank');  
    document.body.append(downloadLink); 
    downloadLink[0].click(); 
}
Jamshid answered 21/4, 2015 at 9:9 Comment(0)
L
5

From react-admin:

function downloadCsv(csv, filename) {
    const fakeLink = document.createElement('a');
    fakeLink.style.display = 'none';
    document.body.appendChild(fakeLink);
    const blob = new Blob([csv], { type: 'text/csv' });
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
        // Manage IE11+ & Edge
        window.navigator.msSaveOrOpenBlob(blob, `${filename}.csv`);
    } else {
        fakeLink.setAttribute('href', URL.createObjectURL(blob));
        fakeLink.setAttribute('download', `${filename}.csv`);
        fakeLink.click();
    }
};

downloadCsv('Hello World', 'any-file-name.csv');
Lalise answered 21/10, 2020 at 12:23 Comment(1)
your code creates a csv file with a single row, regardless of size of the dataRothrock
O
2

Here's how I download CSV files on the client side in my Java GWT application. Special thanks to Xavier John for his solution. It's been verified to work in FF 24.6.0, IE 11.0.20, and Chrome 45.0.2454.99 (64-bit). I hope this saves someone a bit of time:

public class ExportFile 
{

    private static final String CRLF = "\r\n";

    public static void exportAsCsv(String filename, List<List<String>> data) 
    {
        StringBuilder sb = new StringBuilder();
        for(List<String> row : data) 
        {
            for(int i=0; i<row.size(); i++)
            {
                if(i>0) sb.append(",");
                sb.append(row.get(i));
            }
            sb.append(CRLF);
        }

        generateCsv(filename, sb.toString());
    }

    private static native void generateCsv(String filename, String text)
    /*-{
        var blob = new Blob([text], { type: 'text/csv;charset=utf-8;' });

        if (navigator.msSaveBlob) // IE 10+
        { 
            navigator.msSaveBlob(blob, filename);
        } 
        else 
        {
            var link = document.createElement("a");
            if (link.download !== undefined) // feature detection
            { 
                // Browsers that support HTML5 download attribute
                var url = URL.createObjectURL(blob);
                link.setAttribute("href", url);
                link.setAttribute("download", filename);
                link.style.visibility = 'hidden';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            }
        }
    }-*/;
}
Oletta answered 28/9, 2015 at 18:43 Comment(0)
S
2

Simply try this, some of the answers here are not handling unicode data and data that has comma for example date.

function downloadUnicodeCSV(filename, datasource) {
    var content = '', newLine = '\r\n';
    for (var _i = 0, datasource_1 = datasource; _i < datasource_1.length; _i++) {
        var line = datasource_1[_i];
        var i = 0;
        for (var _a = 0, line_1 = line; _a < line_1.length; _a++) {
            var item = line_1[_a];
            var it = item.replace(/"/g, '""');
            if (it.search(/("|,|\n)/g) >= 0) {
                it = '"' + it + '"';
            }
            content += (i > 0 ? ',' : '') + it;
            ++i;
        }
        content += newLine;
    }
    var link = document.createElement('a');
    link.setAttribute('href', 'data:text/csv;charset=utf-8,%EF%BB%BF' + encodeURIComponent(content));
    link.setAttribute('download', filename);
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
};
Seafowl answered 27/3, 2019 at 6:46 Comment(0)
S
1

The answers above work, but keep in mind that if you are opening up in the .xls format, columns ~~might~~ be separated by '\t' instead of ',', the answer https://mcmap.net/q/53850/-how-to-export-javascript-array-info-to-csv-on-client-side worked well for me, so long as I used .join('\t') on the arrays instead of .join(',').

Sporozoite answered 26/10, 2016 at 15:46 Comment(1)
works well for .xls files, BTW I have a minor issue, when the text is too long and exceeds the size of the grid the sheet does not look very well, any hint to solve that?Renunciation
N
1

Here's an Angular friendly version:

  constructor(private location: Location, private renderer: Renderer2) {}

  download(content, fileName, mimeType) {

    const a = this.renderer.createElement('a');

    mimeType = mimeType || 'application/octet-stream';

    if (navigator.msSaveBlob) {

      navigator.msSaveBlob(new Blob([content], {
        type: mimeType
      }), fileName);
    }
    else if (URL && 'download' in a) {

      const id = GetUniqueID();

      this.renderer.setAttribute(a, 'id', id);
      this.renderer.setAttribute(a, 'href', URL.createObjectURL(new Blob([content], {
        type: mimeType
      })));

      this.renderer.setAttribute(a, 'download', fileName);

      this.renderer.appendChild(document.body, a);

      const anchor = this.renderer.selectRootElement(`#${id}`);

      anchor.click();

      this.renderer.removeChild(document.body, a);
    }
    else {
      this.location.go(`data:application/octet-stream,${encodeURIComponent(content)}`);
    }
  };
Neese answered 6/9, 2017 at 12:26 Comment(0)
M
1

I use this function to convert an string[][] to a csv file. It quotes a cell, if it contains a ", a , or other whitespace (except blanks):

/**
 * Takes an array of arrays and returns a `,` sparated csv file.
 * @param {string[][]} table
 * @returns {string}
 */
function toCSV(table) {
    return table
        .map(function(row) {
            return row
                .map(function(cell) {
                    // We remove blanks and check if the column contains
                    // other whitespace,`,` or `"`.
                    // In that case, we need to quote the column.
                    if (cell.replace(/ /g, '').match(/[\s,"]/)) {
                        return '"' + cell.replace(/"/g, '""') + '"';
                    }
                    return cell;
                })
                .join(',');
        })
        .join('\n'); // or '\r\n' for windows

}

Note: does not work on Internet Explorer < 11 unless map is polyfilled.

Note: If the cells contain numbers, you can add cell=''+cell before if (cell.replace... to convert numbers to strings.

Or you can write it in one line using ES6:

t.map(r=>r.map(c=>c.replace(/ /g, '').match(/[\s,"]/)?'"'+c.replace(/"/g,'""')+'"':c).join(',')).join('\n')
Muenster answered 17/11, 2017 at 0:1 Comment(0)
I
1

Download CSV File

  let csvContent = "data:text/csv;charset=utf-8,";
  rows.forEach(function (rowArray) {
    for (var i = 0, len = rowArray.length; i < len; i++) {
      if (typeof (rowArray[i]) == 'string')
        rowArray[i] = rowArray[i].replace(/<(?:.|\n)*?>/gm, '');
      rowArray[i] = rowArray[i].replace(/,/g, '');
    }

    let row = rowArray.join(",");
    csvContent += row + "\r\n"; // add carriage return
  });
  var encodedUri = encodeURI(csvContent);
  var link = document.createElement("a");
  link.setAttribute("href", encodedUri);
  link.setAttribute("download", "fileName.csv");
  document.body.appendChild(link);
  link.click();
Insincerity answered 15/10, 2019 at 8:22 Comment(1)
This looks like it will fail if entries are NOT strings, since you still end up calling replace on them, and you never convert Numbers to strings. Also, it removes all kinds of characters from the strings we might want to preserve!Playwriting
C
1

If you are looking for a really quick solution you can also give a chance to this small library which will create and download CSV file for you: https://github.com/mbrn/filefy

Usage is very simple:

import { CsvBuilder } from 'filefy';

var csvBuilder = new CsvBuilder("user_list.csv")
  .setColumns(["name", "surname"])
  .addRow(["Eve", "Holt"])
  .addRows([
    ["Charles", "Morris"],
    ["Tracey", "Ramos"]
  ])
  .exportFile();
Cultivar answered 29/5, 2020 at 7:24 Comment(0)
B
0

In case anyone needs this for knockout js, it works ok with basically the proposed solution:

html:

<a data-bind="attr: {download: filename, href: csvContent}">Download</a>

view model:

// for the download link
this.filename = ko.computed(function () {
    return ko.unwrap(this.id) + '.csv';
}, this);
this.csvContent = ko.computed(function () {
    if (!this.csvLink) {
        var data = ko.unwrap(this.data),
            ret = 'data:text/csv;charset=utf-8,';

        ret += data.map(function (row) {
            return row.join(',');
        }).join('\n');

        return encodeURI(ret);
    }
}, this);
Burglar answered 3/2, 2016 at 19:49 Comment(0)
C
0

I added to Xavier Johns function to also include the field headers if needed, uses jQuery though. The $.each bit will need changing for a native javascript loop

function exportToCsv(filename, rows, headers = false) {
    var processRow = function (row) {
        row = $.map(row, function(value, index) {
            return [value];
        });
        var finalVal = '';
        for (var j = 0; j < row.length; j++) {
            if(i == 0 && j == 0 && headers == true){
                var ii = 0;
                $.each(rows[i], function( index, value ) {
                    //console.log(index);
                    var fieldName = index === null ? '' : index.toString();
                    //console.log(fieldName);
                    var fieldResult = fieldName.replace(/"/g, '""');
                    //console.log(fieldResult);
                    if (fieldResult.search(/("|,|\n)/g) >= 0){
                        fieldResult = '"' + fieldResult + '"';
                    }
                    //console.log(fieldResult);
                    if (ii > 0){
                        finalVal += ',';
                        finalVal += fieldResult;
                    }else{
                        finalVal += fieldResult;
                    }
                    ii++;
                    //console.log(finalVal);
                });
                finalVal += '\n';
                //console.log('end: '+finalVal);
            }
            var innerValue = row[j] === null ? '' : row[j].toString();
            if (row[j] instanceof Date) {
                innerValue = row[j].toLocaleString();
            };
            var result = innerValue.replace(/"/g, '""');
            if (result.search(/("|,|\n)/g) >= 0){
                result = '"' + result + '"';
            }
            if (j > 0){
                finalVal += ',';
                finalVal += result;
            }else{
                finalVal += result;
            }
        }
        return finalVal + '\n';
    };
    var csvFile = '';
    for (var i = 0; i < rows.length; i++) {
        csvFile += processRow(rows[i]);
    }
    var blob = new Blob([csvFile], { type: 'text/csv;charset=utf-8;' });
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    }else{
        var link = document.createElement("a");
        if (link.download !== undefined) { // feature detection
            // Browsers that support HTML5 download attribute
            var url = URL.createObjectURL(blob);
            link.setAttribute("href", url);
            link.setAttribute("download", filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}
Civilly answered 20/9, 2016 at 12:25 Comment(1)
we know you know Angular, but who asked for it as a solution here ?Sigismundo
D
0

This is a modified answer based on the accepted answer wherein the data would be coming from JSON.

            JSON Data Ouptut:
             0 :{emails: "SAMPLE Co., [email protected]"}, 1:{emails: "Another CO. , [email protected]"}


            JS:
            $.getJSON('yourlink_goes_here', { if_you_have_parameters}, function(data) {
            var csvContent = "data:text/csv;charset=utf-8,";
            var dataString = '';
             $.each(data, function(k, v) {
                dataString += v.emails + "\n";
             });

            csvContent += dataString;

            var encodedUri = encodeURI(csvContent);
            var link = document.createElement("a");
            link.setAttribute("href", encodedUri);
            link.setAttribute("download", "your_filename.csv");
            document.body.appendChild(link); // Required for FF

            link.click();
        });
Dicker answered 21/9, 2016 at 13:18 Comment(0)
C
0

This library helps a lot: https://www.npmjs.com/package/json-to-csv-in-browser

It automatically convert an array of jsons to an csv file and it even gives you the download functionality in case you want to prompt the web user to download the csv file. It works like a charm with very little code.

import { JsonArray, download } from 'json-to-csv-in-browser'

const arr = [
    {name : ` vader`, age : 53},
    {name : "what", age : 38},
    {name : "ever", age : 22}
]
const jsonArray = new JsonArray(arr);
const str = jsonArray.convertToCSVstring();
download("my.csv", str);

Cheers!

EDIT: testing it a little bit more, it doesn't work that well if your values have comma on them

Cly answered 12/7, 2022 at 4:10 Comment(1)
this library is for TypeScriptBrentbrenton
T
0

Here is very good video on how to do it: https://www.youtube.com/watch?v=JPxzeG4N5nQ

its 13 min and this guy is 'stright to the point'... i.e. NOT a typical youtuber

Fundamentally you want CSV in the following format: heading1, heading2, heading3 \n value1, value2, value3 \n value1a, value2a, value3a \n etc.

so the whole tutorial is about achieving this.

then for download you just create a Fake Anchor tag that you auto click on, and then delete the anchor tag.

just watch the tutorial, its all there.

Tilley answered 1/4, 2023 at 21:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.