A way to generate and download CSV files client-side #175

Open
mholt opened this Issue Feb 23, 2015 · 35 comments

Projects

None yet
@mholt
Owner
mholt commented Feb 23, 2015

There seems to be a lot of confusion about how to properly generate a file client-side and have it downloaded in modern browsers. I know how to do it (it's not hard, but it uses a modern web API) and it could potentially be a valuable addition to the library.

Something like:

Papa.download(Papa.parse(csv), "data.json");   // download JSON file
Papa.download(Papa.unparse(data), "data.csv"); // download CSV file

Might take no more than about 20-30 lines of code. Should I do it?

@bluej100
Collaborator

My impression is that it's currently not possible in Safari: see eligrey/FileSaver.js#12 . I think it's an awesome feature, though, and would support including it with that caveat noted.

@mholt
Owner
mholt commented Feb 23, 2015

This seems to work in all modern browsers:

var blob = new Blob([csvString]);
if (window.navigator.msSaveOrOpenBlob)  // IE hack; see http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
    window.navigator.msSaveBlob(blob, "filename.csv");
else
{
    var a = window.document.createElement("a");
    a.href = window.URL.createObjectURL(blob, {type: "text/plain"});
    a.download = "filename.csv";
    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);
}

I used this for a project at my last work. If there's no objection, I'll be happy to build it into 4.2 or something.

@bluej100
Collaborator

Very cool.

@Gribbs
Gribbs commented May 13, 2015

Yes please! Awesome library. But yeah, really hard to find any clear documentation on how to get the Papa.unparse(data) into a csv file

@JedWatson

Looks like this didn't go anywhere, but it would be awesome if it did land in a new version. I'll use your snippet in the meantime, @mholt - thanks!

@mholt
Owner
mholt commented Oct 27, 2015

I haven't abandoned this -- still on my list for the next version, along with a million other little fixes. Just been busy with another project the last few months. Gotta encrypt the Web!!

@JedWatson

Awesome, thanks for the update and totally understand!

@jhawthor
jhawthor commented Dec 5, 2015

Here is what I changed to based on the Google Article here: https://developers.google.com/web/updates/2012/06/Don-t-Build-Blobs-Construct-Them?hl=en
Note that I do not need to append and remove the link from the body.

var csvData = new Blob([str], {type: 'text/csv;charset=utf-8;'});
var csvURL = window.URL.createObjectURL(csvData);
var tempLink = document.createElement('a');
tempLink.href = csvURL;
tempLink.setAttribute('download', 'ActiveEvent_data.csv');
tempLink.click();
@mholt
Owner
mholt commented Dec 5, 2015

Oooo, that's nice. Thanks! Will need to check browser compatibility, but I like that better.

@jhawthor

Update: Fix for IE11

var csvData = new Blob([str], {type: 'text/csv;charset=utf-8;'});
var csvURL =  null;
if (navigator.msSaveBlob) {
    csvURL = navigator.msSaveBlob(csvData, 'download.cv');
} else {
    csvURL = window.URL.createObjectURL(csvData);
}
var tempLink = document.createElement('a');
tempLink.href = csvURL;
tempLink.setAttribute('download', 'download.cv');
tempLink.click();
@AkshayHarshe

This is a great discussion!! This helped me a lot.. Thanks everyone who contributed to this thread.

@lneffa
lneffa commented Jan 27, 2016

๐Ÿ‘ yes this would be a helpful add to the api, considering the newer HTML 5 file api

@hamidzokaee

Thank you for your efforts. Please publish version 4.2 as soon as possible.

@mholt
Owner
mholt commented Feb 8, 2016

I want to, but right now this project is being squeezed out by another project of mine that has funding. (Gotta pay the bills, you know.) If somebody is willing to fund the development of this project then I will be able to afford to put time into it sooner!

@hamidzokaee

hi
I can not help you. I love your work. Thank

On Mon, Feb 8, 2016 at 11:27 PM, Matt Holt notifications@github.com wrote:

I want to, but right now this project is being squeezed out by another
project of mine that has funding. (Gotta pay the bills, you know.) If
somebody is willing to fund the development of this project then I will be
able to afford to put time into it sooner!

โ€”
Reply to this email directly or view it on GitHub
#175 (comment).

@mholt
Owner
mholt commented Feb 8, 2016 edited

That's alright, thank you! I'll try to seek funding for this before the end...

@jessewmc
jessewmc commented Mar 3, 2016

Unfortunately, none of these solutions work in Safari 9.0.3 for the same reasons @bluej100 posted above. I just tested them all, and they all still open raw text in a new browser tab and do not download.

@EliCDavis EliCDavis referenced this issue in EliCDavis/PyChart Mar 5, 2016
Open

Proper downloading of CSV #8

@SergMuller

jesse, it may work if you change "text/csv" to "attachment/csv", but the file name would be just some default one.

@Ginolan
Ginolan commented Mar 17, 2016

If you use dispatchEvent of a new MouseEvent click, instead .click(), it will work also on Internet Explorer and Edge(so you can accomunate code, without use mssaveblob). The only browser i have problem to get working is Safati. I don't have any idea about the way to make it work there.

@Am1rr3zA

I am having the same issue with type = 'application/vnd.ms-excel'; I am on safari 9.0.3 and your solution doesn't work.

@keemor
keemor commented Mar 25, 2016

For modern browsers solution goes like this, tested: IE11, FF & Chrome

var csvData = new Blob([arg.data], {type: 'text/csv;charset=utf-8;'});
//IE11 & Edge
if (navigator.msSaveBlob) {
    navigator.msSaveBlob(csvData, exportFilename);
} else {
    //In FF link must be added to DOM to be clicked
    var link = document.createElement('a');
    link.href = window.URL.createObjectURL(csvData);
    link.setAttribute('download', exportFilename);
    document.body.appendChild(link);    
    link.click();
    document.body.removeChild(link);    
}
@orenmagen100

any news about safari? not found a valid solution for safari...

@bradleybeddoes bradleybeddoes referenced this issue in ausaccessfed/reporting-service May 16, 2016
Open

Reports must be able to export data as CSV #145

@export-mike

@keemor so does this need to be appended to the document.body for support reasons?

@mockpit
mockpit commented Jun 13, 2016

Has anybody found a way to handle this in Safari?

@SergMuller
SergMuller commented Jun 13, 2016 edited

My data was generated as a JSON and fetched by AJAX to a front-end app. Then it had options of dispaying as a table and downloading a file.The original all-JS solution had to be dropped because it didn't show consistent results across browsers (what my customers insisted on).
I ended up using a sub-form that will drop that content to a tiny PHP script, that will just stick a proper header and return right back. I didn't want to over-complicate API to adopt a set of methods for returning CSV.

HTML

<form id="fileDownload" method="post" action="index.php?p=reportsGenerateFile">
<input type="hidden" name="filename" value="default.csv">
<input type="hidden" name="data_csv" value="">
</form>

JS

$('#fileDownload input[name="filename"]').val(filename);
$('#fileDownload input[name="data_csv"]').val(encodeURI(csvContent.join('\n')));
$('#fileDownload').submit();
$('#fileDownload input[name="filename"]').val('default.csv');
$('#fileDownload input[name="data_csv"]').val('');

PHP

if (array_key_exists('filename', $_POST)) $filename = $_POST['filename'];
else $filename = 'default.csv';

if (array_key_exists('data_csv', $_POST)) $filecontent = urldecode($_POST['data_csv']);
else $filecontent = '';

header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$filename\"");
echo $filecontent;
@orenmagen100

@dannynoeloconnor
managed to get it work in Safari 9 for a month (I'm using angular but it can work without) with:

this.$window.open('URL_TO_SERVER_ENDPOINT_?$FILENAME_PARAM', '_self');

but I think the last safari update ruined it. Doesn't work again.

@raza2022
raza2022 commented Aug 2, 2016

@mholt Any update on that ?

@mholt
Owner
mholt commented Aug 2, 2016

Nope, sorry :-/ I haven't worked on this project in a while, been very busy with other things.

@raza2022
raza2022 commented Aug 3, 2016 edited

@mholt can i submit PR, code checked with E11, FF Chrome & Safari 9

@mholt
Owner
mholt commented Aug 3, 2016

Sure, I have time for a quick look at a PR.

@whoisglover

@raza2022 any luck with pull request? Banging my head against same issue with Safari 9

@Philip-Nunoo

_downloadCSV: function(csv) {
var blob = new Blob([csv]);
var a = window.document.createElement("a");
a.href = window.URL.createObjectURL(blob, {type: "text/plain"});
a.download = "contacts.csv";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}

@datafunk

In case it's any help, check this out: http://stackoverflow.com/questions/16376161/javascript-set-file-in-download which evolved into https://github.com/rndme/download/

Doesn't seem to work in Safari (Safari's fault I think), but I used it in my project with the proviso of limited browser support (it's a utility for a small group with specific needs).

@suhailtaj
suhailtaj commented Oct 18, 2016 edited

Works for Safari Version 10.0 or above, Google Chrome Version 54.0.2840.59 or above and Mozilla Firefox.

Used node-rest-client to overcome CORS and basic authentication on API server.

Created a Node Proxy and set headers as:

router.get("/downloadList/:Id/:fileName", function(req, res) {
    // Set hedders to CSV and giving file name
    res.setHeader('Content-disposition', 'attachment; filename=' + req.params.fileName + '.csv');
    res.set('Content-Type', 'text/csv');
    var url = 'https://' + host_name + '/v1/downloadCsv/' + req.params.Id;
    client.get(url, function(data) {
        dateReceived(data);
    });

    function dateReceived(data) {
        var outerData = data;
        res.send(outerData);
    }
});

From client side, request pointing to node JS route:

function downloadCSV(Id) {
 // Create file name and pass it to nodeJS
 var fileName = "yourFileName";
 var e = document.createElement('a');
 e.href = window.location.origin + "/api/v1/home/downloadList/" + Id + "/" + fileName;
 e.target = '_blank';
 document.body.appendChild(e);
 e.click();
 document.body.removeChild(e);
}
@elfenheart
elfenheart commented Nov 1, 2016 edited

With the multiple solutions posted here, have anybody tried downloading a large file? There is an existing bug in V8 where Blob size is limited to 500mb.

ref: https://bugs.chromium.org/p/chromium/issues/detail?id=375297

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment