使用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方法也是按照這個方案實施的,並且作了更多兼容,能夠方便你們參考。但他在資源釋放的地方作的還需改進,也但願你們注意。

參考文檔

相關文章
相關標籤/搜索