JavaScript生成CSV,以及中文乱码问题

由于工作的原因经常需要将表格导出成CSV格式,之前这类的工作都是交由后端处理的,这次由于是做一个单纯的前端工具,所以不想麻烦后台大神,尝试了一次通过JavaScript生成CSV。其实整个过程通按照Baidu/Google上搜索出来的方案就可以流畅的完成,但是实际使用的时候,遇到了一些小问题,比如说中文乱码等,虽然折腾了半个小时用很hack方法解决了,但是整体回顾下来还是蛮有意思的,这里简单的整理一下。

Data URLs

首先需要介绍一下Data URLs,其允许通过URL地址的方式存储文件,我们的CSV也是利用了这个技术,将CSV的内容转成URL,然后在新窗口打开,这样浏览器默认就会去下载这个CSV文件。

Data URLs的默认语法如下:

data:[<mediatype>][;base64],<data>

  • data:是前缀
  • mediatype: 文件的MIME类型,比如image/jpge对应JPGE文件,默认值为text/plain;charset=US-ASCII
  • base64: 文件内容是否base64格式的
  • data: 文件的正文内容

举例如下,可以在浏览器中输入下面的地址以测试:

data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D
data:text/html,<script>alert('hi');</script>

说到这里,大家一定已经有答案了,对于下面的内容

姓名 年龄
Mofei 18

我们只需要组装如下内容即可

姓名,年龄\nMofei,18

加上csv的默认头是text/csv;charset=utf-8,于是我们就得到了下面的内容

data:text/csv;charset=utf-8,姓名,年龄\nMofei,18;

在浏览器里面打开试试,结果悲剧了。。。。

主要原因是URL中我们用了中文等一些浏览器处理起来比较吃力的东西,那么为了解决这个问题,我们尝试把后面的内容encodeURL处理一下encodeURI('姓名,年龄\nMofei,18')

data:text/csv;charset=utf-8,%E5%A7%93%E5%90%8D,%E5%B9%B4%E9%BE%84%0AMofei,18

这下总归好了吧!浏览器打开看看,结果。。。

虽然格式没问题了,但是中文。。惨不忍睹。。。

JavaScript生成CSV中文乱码问题

看到中文乱码,大家第一时间想到的是chartset,但是我们明明设置了charset=utf-8呀,为什么还是乱码?

经过查证之后,发现问题可能出现在BOM上,虽然我们指定了文本类型,但是文件的类型在URL中却无法设定,我们需要将文档的模式也设置成可以识别中文的。

查阅了相关资料之后终于找到了一个神奇的解决方案\uFEFF,加入这个字符之后,可以改变文档的BOM格式,于是乎我们的代码就变成了:

data:text/csv;charset=utf-8,\uFEFF%E5%A7%93%E5%90%8D,%E5%B9%B4%E9%BE%84%0AMofei,18

这样中文乱码的问题就解决了,但是这样下载下来的文件名为下载.csv,那么我们是否可以自定义下载的文件名呢?

自定义CSV文件名

其实这个方法也挺变态的,大部分人按照惯性思维会尝试在Data URLs中做手脚,其实解决这个问题我们可以曲线救国,就是通过a标签的download属性,顾名思义这个属性可以指定我们下载文件的文件名,于是乎我们就有了下面的代码:

var a = document.createElement('a');
a.href='data:text/csv;charset=utf-8,\uFEFF%E5%A7%93%E5%90%8D,%E5%B9%B4%E9%BE%84%0AMofei,18';
a.download="Mofei的CSV.csv";
a.click();

原理很简单,就是生成一个带download属性的a标签,然后我们通过JS模拟点击实现下载的目的。

单元格中的逗号问题

正如我们之前描述,每个单元格中间需要用逗号分割,但是如果单元格内也需要有逗号如何处理呢?

作为程序员,第一个想到了用转移符,然后写成了下面的字段:

Mofei\,Zhu,18

期望能成为

姓名 年龄
Mofei,Zhu 18

结果生无可恋的展示成了:

姓名 年龄
Mofei Zhu 18

原因其实出在了encudeURI上,这个不细说,后来尝试按照字符串来处理,就是把字段用双引号包裹起来变成了

"Mofei\,Zhu",18

果然不出所料的成功了,CSV会自动把双引号之间的内容当成一个单元格处理,皆大欢喜,开心的赶紧去小卖部买只雪糕吃,但是突然转念一想,既然CSV会把双引号当成单元格的边界,那么如果我单元格里也需要双引号怎么办呢?

比如我们希望在Mofei之前加一个 "super man" 我们尝试了下面的方法,

" \"super nam\" Mofei,Zhu",18

结果:

姓名 年龄
super nam" Mofei Zhu" 18

吓得雪糕也掉了。。。。

经过尝试发现双引号在CSV中我们可以用双双引号来表示,即:

" ""super nam"" Mofei,Zhu",18

这下结果就皆大欢喜了

姓名 年龄
"super nam" Mofei,Zhu 18

(赶紧捡起雪糕继续吃。。。 T_T)

END

总的来说,JavaScript生成CSV还是挺简单的,只不过里面涉及到了太多的奇技淫巧,这里简单的记录一下,供参考。

106840
  • logo
    • HI, THERE!I AM MOFEI

      (C) 2010-2024 Code & Design by Mofei

      Powered by Dufing (2010-2020) & Next.js

      IPC证:沪ICP备2022019571号-1