前端可以使用JavaScript在客戶端下載包含頁面數據的文件,這里以下載CSV格式文件為例,代碼如下:
function downloadData(data, filename, type) {
var file = new Blob(["\ufeff" + data], { type: type });
if (window.navigator.msSaveOrOpenBlob)
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else {
// Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
讓我們解釋一下這段代碼:
(一)downloadData 函數
首先,downloadData
函數接收三個參數:
data
:需要下載的數據(注意數據格式需要符合csv格式規范);filename
:需要下載的文件名,注意需要添加符合數據格式的文件名的后綴,這提示了操作系統應該以何種方式打開;type
:代表了將會被放入到Blob
對象中的數組內容的MIME類型;
(二)Blob 構造函數
接着,我們來解析下面這條語句:
var file = new Blob(["\ufeff" + data], { type: type });
可以看到,我們使用了Blob
構造函數,創建了一個Blob實例對象file
。Blob
構造函數有什么用呢?它接收的兩個參數是什么?讓我們在下面一一作答:
① Blob 構造函數的作用
Blob構造函數會根據傳入的數組參數構造出一個新的Blob對象實例,該對象實例的值由以下兩步生成:
- Blob構造函數會將第一個參數,即一個數組內的所有值串聯起來;
- Blob會將被串聯的值轉換為二進制編碼的數據然后返回;
注意,Blob對象實例的值是不可變的,它只有兩個只讀的屬性:size
:表示對象中所包含數據的大小(單位是字節),以及type
:值是一個字符串,表示該對象實例所包含數據的MIME類型(即我們傳入Blob構造函數的第二個參數中指定的type
值,默認為""
)。
② Blob 構造函數接收的參數
Blob構造函數接收的第一個參數為數組類型,數組內的所有值會在實例化時被串聯。若傳入數組的值中有DOMString
類型的值,則會被編碼為UTF-8
格式。而為了讓導出的CSV格式文件在Excel中打開時,中文不出現亂碼,需要在數組的首位添加一個BOM(Byte Order Mark “字節次序標記”)頭\ufeff
。
BOM是一個不可見的字符,它是ES5新增的空白符,在Unicode3.2之前,\uFEFF
表示“零寬不換行空格(Zero Width No-Break Space)”,但在Unicode3.2之后,新增了\u2060
表示零寬不換行空格,\uFEFF
就只用來表示字節次序的標記了。而在Microsoft中,其記事本程序發明了一種UTF-8變體(Python 2.5稱為“utf-8-sig”)以提高可檢測到UTF-8編碼的可靠性,該編碼要求在任何Unicode字符被寫入之前都需要編寫一個UTF-8編碼的BOM(看起來像是一個字節序列:0xef,0xbb,0xbf)。
Blob構造函數接收的第二個參數是一個對象,該對象有以下兩個屬性:
type
:默認值為""
,它表示第一個參數內,數組內容的MIME類型;options
:這個屬性目前還沒有被很好的支持,所以不用管它;
總之,我們通過new Blob()
獲得了一個Blob類型的二進制文件。
(三)下載文件
接着我們要做的便是下載我們生成好的文件了,有兩種方式,第一種方式最簡單,因為IE瀏覽器直接提供了下載文件的接口window.navigator.msSaveOrOpenBlob(file, filename);
,正如函數簽名所示,我們只需要向函數中傳入我們生成好的二進制文件,以及文件名就可以讓瀏覽器自動下載文件了。
拋下IE瀏覽器,要實現文件下載就稍微麻煩些,我們需要通過創建一個a標簽,並模擬點擊這個a標簽實現文件下載,讓我們將與之相關的代碼貼在下面:
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
注意這段代碼的第二行,我們會發現一個新的API:URL.createObjectURL()
函數,這個函數接收一個參數,一個Blob對象,並為該對象生成一個指向該對象的URL對象,需要注意的是只要當下文檔沒有被關閉,該URL對象就會一直存儲在內存中不會被回收,因此一旦確定不再需要該URL對象,一定要及時使用URL.revokeObjectURL()
清理。
以上,我們就解釋了在前端以CSV格式下載數據的方法和原理,需要注意的是,該段代碼只適用於IE10及以上的現代瀏覽器,因為無論是Blob構造函數還是URL.createObjectURL()
API都只有這些瀏覽器提供支持。