JavaScript to generate CSV, and the Chinese garbled problem

Sorry, this article is not written in English and has not been translated into English yet .You can view this article in English with Google Translate, or please come back later.

由于工作的原因经常需要将表格导出成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还是挺简单的,只不过里面涉及到了太多的奇技淫巧,这里简单的记录一下,供参考。

100660
  • logo
    • HI, THERE!I AM MOFEI

      (C) 2010-2024 Code & Design by Mofei

      Powered by Dufing (2010-2020) & Express

      IPC证:沪ICP备2022019571号-1