前阵子跟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
ÃæLþù«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வணà®à¯à®à®®à¯à¸ªà¸§à¸±à¸ªà¸à¸µä½ 好ê°ì§ë§1234ClicktoeditcellsPK
ÃæLÜè¯ÏDDxl/workbook.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
ÃæL0kÞÞxl/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
ÃæLÐ?dÝ--xl/_rels/workbook.xml.rels<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
PK
ÃæLÖ|ZZdocProps/core.xmlPK
ÃæLþù«44docProps/app.xmlPK
ÃæLTÄ8ããëxl/worksheets/sheet1.xmlPK
ÃæLÜè¯ÏDDxl/workbook.xmlPK
ÃæL0kÞÞu xl/theme/theme1.xmlPK
ÃæLUôZZ
'xl/styles.xmlPK
ÃæL÷Â00 ,[Content_Types].xmlPK
ÃæLJjùLLj3_rels/.relsPK
ÃæLÐ?dÝ--ß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 (不晓得是不是可以导出音视频...)