我们需要一个模板文件供用户进行下载,这个文件是csv类型的,大小很小,一开始是想要放到CDN上,上传COS,然后生成a链接下载,地址是这样的 https://xxx.myqcloud.com/csv/xxx.csv

这里有一个问题,在 Safari 浏览器中直接打开了该文件而不是下载,原因是 http header 当中的 Content-Disposition 没有设置成 attachement;filename="xxx.csv",所以浏览器就会按照 Content-Type: text/csv 来进行解析,不同浏览器对该类型返回解析方法不一样, Safari 会打开(以 Content-Disposition: inline 形式),而 Chrome 等其它浏览器会下载。

为了解决这个问题,最后采取了 Data URLs 的方案。首先,执行 uuencode -m xxx.csv 进行 base64 位编码生成 csv 文件的 Data URL,然后将下载地址替换为该 Data URL 。

或者如果需要下载的csv为字符串文本的话,可以使用 js window.btoa() 方法创建一个 base64 编码的字符串。但是 window.btoa() 方法只支持 Latin1 字符,遇到不支持的字符会报错 Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range. 所以需要先通过 encodeURLCompnentd 编码一下。

1
`data:text/csv;base64,77u/${window.btoa(unescape(encodeURIComponent('字符串')))}`

点击下载

77u/ 是 UTF-8 的 BOM(0xEF 0xBB 0xBF)经过 base64 编码过的代码,如果不添加的话在 Micro Excel 中会显示为乱码。

1
2
<a target="_blank" download="测试.csv" rel="noreferrer noopener"
href="data:text/csv;base64,77u/5a2X56ym5Liy">点击下载</a>

这样就完美解决了 Safari 浏览器不会下载的问题,而且文件名也可以进行国际化支持,展示更加友好。

需要注意的是,这种方案只适用于小文件下载,因为该方案实际上相当于把文件变成了页面代码的一部分,如果文件过大,会使得编译后的代码文件过大,对页面加载性能会有负担。