如何将 JavaScript 数组信息导出为 CSV(客户端)?

778

我知道有很多类似的问题,但我需要使用JavaScript来完成。我正在使用Dojo 1.8,并且已经将所有属性信息存储在一个数组中,它看起来像这样:

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

你有什么想法可以在客户端将此导出为CSV吗?

31个回答

19

虽然这是一个有很多好答案的老问题,但以下方法使用两个流行库来解决问题。有些答案提到了Papa Parse,但将下载部分自己实现。通过结合 Papa Parse 和 FileSaver.js,你可以尝试以下代码:

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

unparseconfig 选项在此处描述:here


在我看来,基于维护的库的功能是最短(错误率较低)且可能最健壮的跨浏览器方法。谢谢。 - Andreas Covidiot

14

这就是您要的:

<!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>  

1
很棒的答案。我给这个点赞,并将其作为被接受的答案,因为原先的答案把所有内容都放在一个单列中。这样分成不同的列,而且支持像 JSON 数据格式这样非常有用。 - Hoser
当链接首次添加到文档主体中并调用单击时,此方法有效。然后将其从DOM中删除。 - Jay Dubal
1
好的回答,唯一的缺点是当数据有列分隔符“,”时无法正常工作,例如地址:“10 Infinite loop lane,Room 56”,请注意lane后面的逗号。我建议您使用PapaParse [链接](http://papaparse.com/demo)将数据转换为CSV,然后使用上面的downloadCSV方法来下载实际文件。 - Philip E
这对我来说完美地运作。只有一个问题,我的数组中有一些数字,例如“000002342”,但是当导出到CSV时,前导零会被删除。有没有什么方法可以防止这种情况发生? - Aakarsh Dhawan

8

使用ES6的箭头函数:

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

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

如果有人需要的话,可以使用react-csv来实现。


3
react-csv库非常好用,是任何使用模块的人的绝佳解决方案。 - Matt Parrilla
这个程序没有考虑到CSV文件中存在逗号的字段的情况。 - unpollito

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

8
以下是本地JavaScript解决方案。

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>


7

这里有很多自己编写的解决方案来将数据转换为CSV,但几乎所有这些方案都会在正确格式化数据时出现各种警告,而不会使Excel或类似软件出错。

为什么不使用一些已被证明有效的东西:Papa Parse

Papa.unparse(data[, config])

然后,只需将此与本地下载解决方案之一结合使用,例如@ArneHB的解决方案看起来不错。


6
这里有两个问题:
  1. 如何将数组转换为csv字符串
  2. 如何将该字符串保存到文件中
第一个问题的所有答案(除了Milimetric的答案)似乎都太过复杂。而Milimetric的答案并不能满足其他要求,例如在字符串周围加上引号或将对象数组转换为csv字符串。
以下是我的建议:
对于简单的csv,使用map()和join()即可:
    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

这种方法还允许您在内部连接中指定除逗号以外的列分隔符,例如制表符:d.join('\t')

另一方面,如果您想正确地用引号""将字符串括起来,那么可以使用一些JSON技巧:

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

如果您有一个对象数组,例如:
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, '');

如果我没记错的话,我相信.replace应该指定花括号和方括号。 - aaronbartell
.replace 是在 values() 返回的字符串上执行的,该方法接受一个对象并返回一个值数组。 - Konstantin
当我尝试你的代码时,发现找不到values()方法。 - aaronbartell
谢谢!在Chrome中,不需要显式地在Object上调用values()就可以工作。我已经更正了这个例子。 - Konstantin

5
创建带有CSV数据的Blob,例如: var blob = new Blob([data], {type: "text/csv"}); 如果浏览器支持保存Blob,例如:if (window.navigator.msSaveOrOpenBlob){},那么可以使用以下代码保存CSV数据:window.navigator.msSaveBlob(blob, 'filename.csv') 如果浏览器不支持保存和打开blob,则可以将csv数据保存为:
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(); 
}

5

来自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');

您的代码会创建一个 CSV 文件,无论数据大小如何,都只有一行。 - Aleks

2

这里是我在Java GWT应用程序中如何在客户端下载CSV文件的方法。特别感谢Xavier John提供的解决方案。它已经在FF 24.6.0,IE 11.0.20和Chrome 45.0.2454.99(64位)中验证可行。希望这能为某人节省一些时间:

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);
            }
        }
    }-*/;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接