前陣子跟server同學討論一個Excel導出的需求,我說JS搞不定,需要server來做,被server同學強行打臉。
今天研究了下,尼瑪,不光可以,還很強大了!
總結:經驗是害人的,尤其是在發展迅速的前端圈兒,and,需要保持饑渴,保持對新技術的敏感度。
注:以下只探討現代瀏覽器
1. 最簡單的Excel導出
原理:js可以通過base64或者blob,把一個包含一個
var tableHtml='
only one |
var oa = document.createElement('a');
oa.href= 'data:application/vnd.ms-excel;base64,'+window.btoa(tableHtml);
oa.download= 'htmltable-base64.xls';//通過A標簽 設置文件名
oa.click();
文件,在js中,除了可以是base64,也可以是一個blob。
- base64形式的文件描述,在js或者html中,就是一個很長的base4字符串
- blob形式的文件描述,在js或者html中,是一個URL形式的字符串,他指向的是瀏覽器內存中的一個文件片段,形如"blob:http://sheetjs.com/f999f57f-b79f-4293-a317-3bbf6ea58788"
blob形式的Excel導出,如下:
//blob URL形式文件下載
var tableHtml='
only one |
oa.href=URL.createObjectURL(excelBlob);
oa.download= 'htmltable-blob.xls';
document.body.appendChild(oa);
oa.click();
毛病:
- 這是個假的excel文件,只有xls格式可以在Excel中打開,xlsx不行。
2. 真正的Excel導出
是的,這里有一個真正的二進制Excel文件導出。
他就是一萬多star的js-xlsx,地址:https://github.com/SheetJS/js-xlsx
我花了兩個多小時,追了好一陣子他的https://github.com/SheetJS/js-xlsx/blob/master/xlsx.js,終于,我搞明白他是什么原理了。
以下拿他的官方demo舉例,http://sheetjs.com/demos/table.html。
從網頁的table DOM到Excel文件的演化過程如下:
2.1 網頁上的table
This
is
a
Test
???????
??????
你好
???
1
2
3
4
Click
to
edit
cells
2.2 sheet JSON
這里,他用一個json來描述了Excel表格中的A1,B1,C1等各個單元格。
{"Sheet JS":{"A1":{"t":"s","v":"This"},"B1":{"t":"s","v":"is"},"C1":{"t":"s","v":"a"},"D1":{"t":"s","v":"Test"},"A2":{"t":"s","v":"???????"},"B2":{"t":"s","v":"??????"},"C2":{"t":"s","v":"你好"},"D2":{"t":"s","v":"???"},"A3":{"t":"n","v":1},"B3":{"t":"n","v":2},"C3":{"t":"n","v":3},"D3":{"t":"n","v":4},"A4":{"t":"s","v":"Click"},"B4":{"t":"s","v":"to"},"C4":{"t":"s","v":"edit"},"D4":{"t":"s","v":"cells"},"!ref":"A1:D4"}}
2.3?未壓縮的zip文件
源碼中的“write_zip_type”方法,它按照標準的電子表格格式協議,把上述JSON轉成了下面的樣子。
如下,很明顯,這里面包含了一些亂碼和一些xml描述。
(這里本著不求甚解的精神,我咨詢了一下我們部門的資深技術專家,他搭眼一看,說這是一個未壓縮的zip。我也懶得輸出一下zip來驗證這個了,他說是,那就是了)
PK
??L?|ZZdocProps/core.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
??Ltù?44docProps/app.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
SheetJSWorksheets1Sheet JSPK
??LT?8??xl/worksheets/sheet1.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
ThisisaTestà?μà?£à?àˉà?à??àˉà?aà?§à?±à?aà?à?μ????¥?ê°ì§?§1234ClicktoeditcellsPK
??Lüèˉ?DDxl/workbook.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
??L0kTTxl/theme/theme1.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
??LU?ZZ
xl/styles.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
??L÷?00[Content_Types].xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
??LJjùLL_rels/.rels<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
??LD?dY--xl/_rels/workbook.xml.rels<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
??L?|ZZdocProps/core.xmlPK
??Ltù?44docProps/app.xmlPK
??LT?8???xl/worksheets/sheet1.xmlPK
??Lüèˉ?DDxl/workbook.xmlPK
??L0kTTu xl/theme/theme1.xmlPK
??LU?ZZ
'xl/styles.xmlPK
??L÷?00 ,[Content_Types].xmlPK
??LJjùLLj3_rels/.relsPK
??LD?dY--?5xl/_rels/workbook.xml.relsPK >D8
2.4 Blob URL
其實,我最感興趣的是這兒。2.3中的一大坨字符串,通過 Uint8Array轉成了無符號數組,并通過new Blob方法,轉成了二進制文件片段,關鍵代碼如下:
functionblobify(strData) {var buf = new ArrayBuffer(strData.length), view = newUint8Array(buf);for (var i=0; i!=strData.length; ++i) view[i] = strData.charCodeAt(i) & 0xFF;returnbuf;
}var excelBlob = new Blob([blobify(data)], {type:"application/octet-stream"});var blobURL=URL.createObjectURL(excelBlob);
最后,通過URL.createObjectURL方法,把blob轉成了,肉眼可見的js和HTML中可以看到的,Blob URL,如下:
blob:http://sheetjs.com/f999f57f-b79f-4293-a317-3bbf6ea58788
尼瑪,一個html轉Excel的庫js,有20170行代碼,恩,不錯,開源萬歲。
3. 總結
看起來,先不說性能如何,上面這些關鍵API利用一下,js應該是可以導出很多種格式的文件了。
- 文本類的,txt html js css xml
- 特定協議的文檔,pdf Excel cvs(看起來word ppt 應該也可以了,懶得去查了)
- 其他各類二進制文件,zip png jpg gif (不曉得是不是可以導出音視頻...)