使用Data URI Scheme优雅的实现前端导出csv

问题描述

项目里须要实现一个导出csv的功能,这是个老生常谈的需求,并且咱们使用的是iview的组件库,按道理说实现起来应该简单,但实则否则,我在作的时候遇到了一些问题。受限于请求须要token后端分页接口性能等缘由不得不放弃iview的导出方式。因此我须要寻找一种可行的、合理的、优雅的导出方案,那就是Data URI Schemejavascript

方案实现

方案介绍

Data URI Scheme是利用HTML标签的hrefsrc属性来实现的。他看起来像是这样的: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

下载
失败-网络错误

clipboard.png

后来google到这个限制是2MB,由于没有官方文档说明,感受2MB的说法不是很肯定,因此去扒了Chromium源码,找到了相关代码:chrome

const size_t kMaxURLChars = 2 * 1024 * 1024;
...
if (!iter->ReadString(&s) || s.length() > url::kMaxURLChars) {
    *p = GURL();
    return false;
}
  1. 变量声明部分源码连接
  2. 变量引用部分源码连接

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对象,因此咱们要看看浏览的支持状况浏览器

clipboard.png

恩,看起来没有问题,那咱们来看看代码实现。

<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的实现

另外,在我写这篇文章的时候我发现iviewexport-csv方法也是按照这个方案实施的,并且作了更多兼容,能够方便你们参考。但他在资源释放的地方作的还需改进,也但愿你们注意。

参考文档

相关文章
相关标签/搜索