项目里须要实现一个导出csv的功能,这是个老生常谈的需求,并且咱们使用的是iview
的组件库,按道理说实现起来应该简单,但实则否则,我在作的时候遇到了一些问题。受限于请求须要token
、后端分页
、接口
、性能
等缘由不得不放弃iview
的导出方式。因此我须要寻找一种可行的、合理的、优雅的导出方案,那就是Data URI Scheme
。javascript
Data URI Scheme是利用HTML标签的href
和src
属性来实现的。他看起来像是这样的:html
<img src="data:image/png;base64,iVBORw0KGgoAAA ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4 //8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU 5ErkJggg==" alt="Red dot" />
或者java
<a href="data:text/csv,something">download</a>
按照这种方案的介绍,咱们把要导出的数据拼接在href
指定位置就能实现导出的需求,代码实现看起来像这样:node
<a href="" download="export.csv" id="export_csv" style="display='none'">download</a>
function export_csv (data) { $('#export_csv').href = 'data:attachment/csv,' + encodeURI(data); $('#export_csv').click(); setTimeout(function () { $('#export_csv').href = ''; }) } export_csv(csv_data_str);
测试发现,妥妥的,没毛病。git
在实践中这个方案是有限制的、不安全的:在chrome的实现中Data URI Scheme容许的URL的最大限制为2MB(其余浏览器这里不作讨论)。
一开始并不知道是超过2MB才会出问题,只是发现:
当在下载的文件比较大(超过2.7MB)的时候Chrome会报这样的错误:github
下载 失败-网络错误
后来google到这个限制是2MB,由于没有官方文档说明,感受2MB的说法不是很肯定,因此去扒了Chromium源码,找到了相关代码:chrome
const size_t kMaxURLChars = 2 * 1024 * 1024; ... if (!iter->ReadString(&s) || s.length() > url::kMaxURLChars) { *p = GURL(); return false; }
2MB的限制算是实锤了,同时发现2010年就有人在Chromium论坛提出2MB过小了了,可是一直讨论到2019年也没有明显的改善(只是改了图片部分)。唉,chromium不改,咱们能怎么办呢?c#
chromium不改,那咱们只能本身想办法了,因而有大牛提出来使用URL.createObjectURL + Blob
来突破这个限制。
借助Blob对象和URL.createObjectURL咱们能够获得一个很短的、并且几乎与内容长度无关的URL:后端
blob:https://xxx.com/0bde569d-20a2-4085-95e6-dcec242962c6
这样就能突破Chrome对Data URI Scheme URL大小的限制了。
固然呢,我没用过URL.createObjectURL
这个方法,也没用过Blob
对象,因此咱们要看看浏览的支持状况浏览器
恩,看起来没有问题,那咱们来看看代码实现。
<a href="" download="export.csv" id="export_csv" style="display='none'">download</a>
function export_csv (data) { const BOM = '\uFEFF'; let blob_obj = new Blob([BOM + data], {type: 'text/csv'}); let download_url = URL.createObjectURL(blob_obj); $('#export_csv').href = download_url; $('#export_csv').click(); setTimeout(function () { // 经过createObjectURL建立的url须要经过revokeObjectURL()来释放 URL.revokeObjectURL(download_url); $('#export_csv').href = ''; }) } export_csv(csv_data_str);
如此,问题解决了,这样就不怕超过2MB的CSV的导出了。
可是Chrome对Blob对象的大小有限制吗?
Good question !
我在chromium Blob的说明文档中找到一个表:
Device | Ram | In-Memory Limit | Disk | Disk Limit | Min Disk Availability |
---|---|---|---|---|---|
Cast | 512 MB | 102 MB | 0 | 0 | 0 |
Android Minimal | 512 MB | 5 MB | 8 GB | 491 MB | 10 MB |
Android Fat | 2 GB | 20 MB | 32 GB | 1.9 GB | 40 MB |
CrOS | 2 GB | 409 MB | 8 GB | 4 GB | 0.8 GB |
Desktop 32 | 3 GB | 614 MB | 500 GB | 50 GB | 1.2 GB |
Desktop 64 | 4 GB | 2 GB | 500 GB | 50 GB | 4 GB |
从这个表中,大概能够看出来在In-Memory Storage
的时候桌面版64位Chrome Blob的上限为2GB(在Chrome 57上限是500MB)。因此如今看来这种方法应该是安全的。至此,这个问题算是完整的解决了。
另外,在我写这篇文章的时候我发现iview
的export-csv
方法也是按照这个方案实施的,并且作了更多兼容,能够方便你们参考。但他在资源释放的地方作的还需改进,也但愿你们注意。