84

I need to export javascript array to excel file and download it I'm doing it in this code. data is a javascript object array.

var csvContent = "data:text/csv;charset=utf-8,";
data.forEach(function(dataMember, index)
{
    dataString = dataMember.join(",");
    csvContent += index < data.length ? dataString+ "\n" : dataString;
}); 

var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "upload_data" + (new Date()).getTime() + ".csv");
link.click();

All this stuff works fine till I have string properties that have non-english characters, like spanish, arabic or hebrew. How can I make an export with all this non-ASCII values?

Benoit Blanchon
  • 13,364
  • 4
  • 73
  • 81
Boltosaurus
  • 2,138
  • 3
  • 21
  • 33

11 Answers11

133

You should add the UTF-8 BOM at the start of the text, like:

var csvContent = "data:text/csv;charset=utf-8,%EF%BB%BF" + encodeURI(csvContent);

It worked for me with Excel 2013.

Demo Fiddle

S.Serpooshan
  • 7,608
  • 4
  • 33
  • 61
Gergő Nagy
  • 1,540
  • 1
  • 11
  • 12
117

You can add the BOM at first, use this code and try

var BOM = "\uFEFF"; 
var csvContent = BOM + csvContent;

and then crate the file headers with the data: "text/csv;charset=utf-8"

Marcelo Lujan
  • 2,142
  • 1
  • 17
  • 12
  • 3
    THIS! Took forever to find something that would work. Thanks! – KyleFarris Sep 29 '16 at 19:02
  • 1
    This worked for me when converting my stuff to a blob and then using the anchor tag click hack to trigger download: `var downloadLink = document.createElement("a"); downloadLink.download = fileNameToSaveAs; downloadLink.href = window.URL.createObjectURL(textFileAsBlob); downloadLink.onclick = function (e) { document.body.removeChild(e.target); }; downloadLink.style.display = "none"; document.body.appendChild(downloadLink); downloadLink.click();` – skrile Oct 12 '16 at 13:31
  • Excel is now showing Unicode characters like `ář` properly. Thanks – Karthik Apr 17 '17 at 09:46
  • 1
    Excellent, this worked for me with plain JavaScript! – Thread Pitt Feb 07 '18 at 11:59
  • 3
    This did the trick for me: csv = 'data:text/csv;charset=utf-8,\uFEFF' + csvContent; – Staafsak Apr 24 '18 at 12:06
  • It's creating for me an empty blank line at the beginning, isn't it?, well I'm using the same approach of injecting those boom characters plus the csv content but using Ruby instead – Heriberto Magaña Sep 09 '18 at 20:44
  • This worked for me too. Thank you! – dpigera Sep 21 '21 at 20:58
18

Excel is really bad at detecting encoding, especially Excel on OSX.

The best solution would be to encode your CSV in the default Excel encoding: windows-1252 (also called ANSI, which is basically a subset of ISO-8859-1).

I put a complete example of how to do that at: https://github.com/b4stien/js-csv-encoding.

The 2 main parts are stringencoding (to encode the content of your CSV in windows-1252) and FileSaver.js (to download the generated Blob).

It looks like:

var csvContent = 'éà; ça; 12\nà@€; çï; 13',
    textEncoder = new TextEncoder('windows-1252');


var csvContentEncoded = textEncoder.encode([csvContent]);
var blob = new Blob([csvContentEncoded], {type: 'text/csv;charset=windows-1252;'});
saveAs(blob, 'some-data.csv');
b4stien
  • 1,810
  • 13
  • 14
  • Amazing, thanks! Went through reams of SO pages and docs looking for something to solve an issue with an excel destroying CSVs after opening and saving them back out on OSX. This was the only thing that worked. – Greg Venech Feb 16 '17 at 22:41
  • 3
    Notice that `TextEncoder` no longer accept any encoding other than utf-8 now. – tsh Jan 31 '18 at 04:00
  • It wont work in the current version that i had to download the version from this link. Thanks for saving it – Shift 'n Tab Feb 27 '18 at 03:10
  • I ran into a similar issue - InDesign's DataMerge obstinately refused to show my special characters, regardless of whether I attempted UTF-8, UTF-16, UTF-16LE, tabs, commas, anything. Using the files in b4stien's repo above, and adapting his example, it worked perfectly! Worth noting that in my case, I only needed to target Chrome on Windows. – James Hill Oct 31 '18 at 10:28
  • Thank you, @b4stien I am looking to find out an encoding for the uploaded CSV file. The users might upload in different languages. How can I find out that? I tried many solutions but nothing seems to work for me. Any help please? – Roshan Oct 09 '20 at 11:47
7

Option 1

use iconv-lite library and encode your output to ascii before send it back to the user. Example:

var iconv = require('iconv-lite');
buf = iconv.encode(str, 'win1255'); // return buffer with windows-1255 encoding

Option 2

Write on the head of the file the BOM header of UTF-8 encoding. Example:

res.header('Content-type', 'text/csv; charset=utf-8');
res.header('Content-disposition', 'attachment; filename=excel.csv'); 
res.write(Buffer.from('EFBBBF', 'hex')); // BOM header

// rest of your code

Option 3

Use base64 url format like data:text/csv;base64,77u/Zm9vLGJhcg0KYWFhLGJiYg==. This method will work on client-side also (IE10+, FF, Chrome, Opera, Safari).

For example:

window.location = "data:text/csv;base64,77u/" + btoa("foo,bar\r\naaa,bbb");
Moshe Simantov
  • 3,937
  • 2
  • 25
  • 35
  • Hey, thanks for your response. Can you please give a full example of option 2? What exactly is .header() method? What exactly is res object? – Boltosaurus Nov 18 '13 at 15:04
  • I'm using [express.js](http://expressjs.com) framework. On plane node.js you can use [setHeader](http://nodejs.org/api/http.html#http_response_setheader_name_value). – Moshe Simantov Nov 27 '13 at 15:24
  • 1
    I'm not talking about node. I'm using pure client-side javascript) – Boltosaurus Nov 28 '13 at 07:58
  • I added a new option for you - option 3. – Moshe Simantov Nov 28 '13 at 10:22
  • btoa is not available in older IE. And doesn't work even in Chrome or FF on a mac: http://jsbin.com/vazazaru/1/ – jwl Mar 04 '14 at 17:36
  • Thanks for the option 2, it works fine. Even if the OP didn't ask for an express solution, it still useful for people coming from google! :) – Getz Apr 18 '16 at 13:55
  • 1
    es.write(new Buffer('EFBBBF', 'hex')); is no longer in use. – Habibul Hasan Nov 29 '18 at 08:38
  • Option 3 worked for my problem which is a bit different than OP, but Google still brought me here. I have a javascript created csv that was breaking the output from hashtag characters in a cell. Base64 encoding fixed this for me. – Don Rzeszut Jun 27 '19 at 20:44
  • 1
    Thanks @MosheSimantov almost 9 years later, you saved my day. The ,77u/ after the base64 solved my Python Databricks streaming export. This was even not mentioned on the BOM WikiPedia pages! – langeleppel Jan 04 '22 at 16:38
7

It is not necessary to use the encodeURIComponent method and glue the data string snippets. Just glue the BOM character in front of the string.

const data = 'öäüÖÄÜ';
const BOM = '\uFEFF';
const blob = new Blob([BOM + data], { type: 'text/csv;charset=utf-8' });

const url = window.URL.createObjectURL(blob);
const linkElem = document.createElement('a');
linkElem.href = url;
linkElem.click();
btx
  • 1,972
  • 3
  • 24
  • 36
2

somehow found Tab-Separated-CSV with utf-16le encoding with BOM works on WIN/MAC Excel

followed b4stien's answer but make a little difference to archive:

var csvContent = 'éà; ça; 12\nà@€; çï; 13',
    textEncoder = new TextEncoder('utf-16le');
var csvContentEncoded = textEncoder.encode([csvContent]);
var bom = new Uint8Array([0xFF, 0xFE]);
var out = new Uint8Array( bom.byteLength + csvContentEncoded.byteLength );
out.set( bom , 0 );
out.set( csvContentEncoded, bom.byteLength );
var blob = new Blob([out]);
saveAs(blob, 'some-data.csv');

with Linux /usr/bin/file tests:

Little-endian UTF-16 Unicode text, with very long lines, with CRLF line terminators
gjchen
  • 21
  • 1
  • 2
    unfortunately this won't work anymore => "Note: Prior to Firefox 48 and Chrome 53, an encoding type label was accepted as a paramer to the TextEncoder object, since then both browers have removed support for any encoder type other than utf-8, to match the spec. Any type label passed into the TextEncoder constructor will now be ignored and a utf-8 TextEncoder will be created." https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder – Emmanuel Touzery Apr 03 '19 at 07:29
  • 1
    I've add success with `const blob = new Blob([new Uint8Array(iconv_lite.encode(csvContent, "utf16-le", {addBOM: true}))]);` and then `saveAs` (from `file-saver`) – Emmanuel Touzery Apr 03 '19 at 08:03
2
 data=`"red","मुकेश"`
 var processdata = "data:text/csv;charset=utf-8,%EF%BB%BF" + encodeURIComponent(data);
  • Please, fell free to expand on your answer. Specifically, try to explain why it solves the question (better than the already massively up-voted answers above). – Xyz Sep 30 '20 at 11:23
1

I've been able to solve my issue with the help of https://stackoverflow.com/a/27975629/5228251 answer

const json2csv = require('json2csv');
const csvExport = (req, res) => {
    var csvContent = json2csv({ data, fields })

    res.setHeader('Content-Type', 'text/csv')
    // just prepend the '\ufeff' to your csv string value
    return res.status(200).send('\ufeff' + csvContent)
}
Ali Nowrouzi
  • 59
  • 1
  • 5
0

B4stien, thank you to you for your answer! After testing several solutions based on charset "utf8", encoding windows-1252 is the only solution that allowed me to keep my accent in Excel 365!

Manetsus, the b4stien's answer and his link were very usefull for my case: i have to export french and german data into csv file: no solution based on "utf8" has worked... Only his solution which use an "ANSI" (window-1252) encoder...

I give his code sample, and you can download the depending encoding-indexes.js, encoding.js and FileSaver.js from the link...

    <!doctype html>
    <html>

    <head>
        <meta charset="utf-8">
        <script type="text/javascript" src="encoding-indexes.js"></script>
        <script type="text/javascript" src="encoding.js"></script>
        <script type="text/javascript" src="FileSaver.js"></script>
    </head>

    <body>
        <a href="#" id="download-csv">Click me to download a valid CSV !</a>

        <script type="text/javascript">
            var csvContent = 'éà; ça; 12\nà@€; çï; 13',
                textEncoder = new CustomTextEncoder('windows-1252', {NONSTANDARD_allowLegacyEncoding: true}),
                fileName = 'some-data.csv';

            var a = document.getElementById('download-csv');
            a.addEventListener('click', function(e) {
                var csvContentEncoded = textEncoder.encode([csvContent]);
                var blob = new Blob([csvContentEncoded], {type: 'text/csv;charset=windows-1252;'});
                saveAs(blob, fileName);
                e.preventDefault();
            });
        </script>
    </body>

    </html>

Nevertheless, as Excel is relatively open in the support of languages and formats, I do not exclude that UTF8 is not supported in my development environment because of the way it is installed ...

Note: I test it with Firefox, Chrome and IE 11 on windows 7, with Excel 365...

Didier68
  • 1,027
  • 12
  • 26
  • Hello, greek characters do not work with this sample code. How to make them exportable as well ? – mtnp Sep 09 '20 at 09:39
0

To export CSV containing multibyte characters and make it readable on text editor and Excel in multiple OS platforms (Windows, Linux, MacOS), the following rules should be applied:

  1. Separate the field with tab instead of comma (so that Excel on MacOS can properly display the generated CSV file)
  2. Encode the string / content with UTF-16 little endian (UTF16-LE) instead of UTF-8
  3. Add byte order mark (BOM) 0xFEFF as specified in RFC2781 section 3.2 at the beginning of the serialized stream to explicitly provide "signature" of content encoded with UTF16-LE

Further elaboration, use cases and sample code with NodeJS can be seen in this article.

mikaelfs
  • 399
  • 3
  • 4
0

This is the solution that worked for me

Reference: Export CSV file -encoded

var csv = 'السلام عليكم, Student name\n';
        
var hiddenElement = document.createElement('a');
        //workaround to support encoding
hiddenElement.href = **'data:text/csv;charset=utf-8,%EF%BB%BF'+encodeURIComponent(csv);**

hiddenElement.download = 'Course Students Progress - ' + new Date().toLocaleString() + '.csv';
hiddenElement.click();
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sarah Tammam
  • 134
  • 2
  • 9