js文件操作之——导出Excel (js-xlsx)

前阵子跟server同学讨论一个Excel导出的需求,我说JS搞不定,需要server来做,被server同学强行打脸。

今天研究了下,尼玛,不光可以,还很强大了!

总结:经验是害人的,尤其是在发展迅速的前端圈儿,and,需要保持饥渴,保持对新技术的敏感度。

注:以下只探讨现代浏览器

1. 最简单的Excel导出

原理:js可以通过base64或者blob,把一个包含一个<table>的<html>串,导出成xx.xls格式。而Excel可以打开html文件。这样看起来,就是一个成功的Excel导出。

var tableHtml='<html><head><meta charset="UTF-8"></head><body><table><tr><td>only one</td></tr></table></body></html>';
//base64 URL形式文件下载
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='<html><head><meta charset="UTF-8"></head><body><table><tr><td>only one</td></tr></table></body></html>';
var excelBlob = new Blob([tableHtml], {type: 'application/vnd.ms-excel'});
var oa = document.createElement('a');
oa.href = URL.createObjectURL(excelBlob);
oa.download = 'htmltable-blob.xls';
document.body.appendChild(oa);
oa.click();

毛病:

- 这是个假的excel文件,只有xls格式可以在Excel中打开,xlsx不行。

扫描二维码关注公众号,回复: 1917473 查看本文章

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"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>PK
ÎæLþù«44docProps/app.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>SheetJS</Application><HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>Worksheets</vt:lpstr></vt:variant><vt:variant><vt:i4>1</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="1" baseType="lpstr"><vt:lpstr>Sheet JS</vt:lpstr></vt:vector></TitlesOfParts></Properties>PK
ÎæLT…Ä8ããxl/worksheets/sheet1.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><dimension ref="A1:D4"/><sheetViews><sheetView workbookViewId="0"/></sheetViews><sheetData><row r="1"><c r="A1" t="str"><v>This</v></c><c r="B1" t="str"><v>is</v></c><c r="C1" t="str"><v>a</v></c><c r="D1" t="str"><v>Test</v></c></row><row r="2"><c r="A2" t="str"><v>வணக்கம்</v></c><c r="B2" t="str"><v>สวัสดี</v></c><c r="C2" t="str"><v>你好</v></c><c r="D2" t="str"><v>가지마</v></c></row><row r="3"><c r="A3"><v>1</v></c><c r="B3"><v>2</v></c><c r="C3"><v>3</v></c><c r="D3"><v>4</v></c></row><row r="4"><c r="A4" t="str"><v>Click</v></c><c r="B4" t="str"><v>to</v></c><c r="C4" t="str"><v>edit</v></c><c r="D4" t="str"><v>cells</v></c></row></sheetData><ignoredErrors><ignoredError numberStoredAsText="1" sqref="A1:D4"/></ignoredErrors></worksheet>PK
ÎæLÜè¯ÏDDxl/workbook.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><workbookPr codeName="ThisWorkbook"/><sheets><sheet name="Sheet JS" sheetId="1" r:id="rId1"/></sheets></workbook>PK
ÎæL0ˆkÞÞxl/theme/theme1.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F497D"/></a:dk2><a:lt2><a:srgbClr val="EEECE1"/></a:lt2><a:accent1><a:srgbClr val="4F81BD"/></a:accent1><a:accent2><a:srgbClr val="C0504D"/></a:accent2><a:accent3><a:srgbClr val="9BBB59"/></a:accent3><a:accent4><a:srgbClr val="8064A2"/></a:accent4><a:accent5><a:srgbClr val="4BACC6"/></a:accent5><a:accent6><a:srgbClr val="F79646"/></a:accent6><a:hlink><a:srgbClr val="0000FF"/></a:hlink><a:folHlink><a:srgbClr val="800080"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Cambria"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Times New Roman"/><a:font script="Hebr" typeface="Times New Roman"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="MoolBoran"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Times New Roman"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/></a:majorFont><a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="DaunPenh"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Arial"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="1"/></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="100000"/><a:shade val="100000"/><a:satMod val="130000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="50000"/><a:shade val="100000"/><a:satMod val="350000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="0"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="38000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst><a:scene3d><a:camera prst="orthographicFront"><a:rot lat="0" lon="0" rev="0"/></a:camera><a:lightRig rig="threePt" dir="t"><a:rot lat="0" lon="0" rev="1200000"/></a:lightRig></a:scene3d><a:sp3d><a:bevelT w="63500" h="25400"/></a:sp3d></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="40000"><a:schemeClr val="phClr"><a:tint val="45000"/><a:shade val="99000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="20000"/><a:satMod val="255000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="-80000" r="50000" b="180000"/></a:path></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="80000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="30000"/><a:satMod val="200000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults><a:spDef><a:spPr/><a:bodyPr/><a:lstStyle/><a:style><a:lnRef idx="1"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="3"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="2"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef></a:style></a:spDef><a:lnDef><a:spPr/><a:bodyPr/><a:lstStyle/><a:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="0"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="1"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="tx1"/></a:fontRef></a:style></a:lnDef></a:objectDefaults><a:extraClrSchemeLst/></a:theme>PK
ÎæLUô”ZZ
xl/styles.xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><numFmts count="1"><numFmt numFmtId="56" formatCode="&quot;上午/下午 &quot;hh&quot;時&quot;mm&quot;分&quot;ss&quot;秒 &quot;"/></numFmts><fonts count="1"><font><sz val="12"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font></fonts><fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles><dxfs count="0"/><tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4"/></styleSheet>PK
ÎæL÷—00[Content_Types].xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Default Extension="xml" ContentType="application/xml"/><Default Extension="bin" ContentType="application/vnd.ms-excel.sheet.binary.macroEnabled.main"/><Default Extension="vml" ContentType="application/vnd.openxmlformats-officedocument.vmlDrawing"/><Default Extension="bmp" ContentType="image/bmp"/><Default Extension="png" ContentType="image/png"/><Default Extension="gif" ContentType="image/gif"/><Default Extension="emf" ContentType="image/x-emf"/><Default Extension="wmf" ContentType="image/x-wmf"/><Default Extension="jpg" ContentType="image/jpeg"/><Default Extension="jpeg" ContentType="image/jpeg"/><Default Extension="tif" ContentType="image/tiff"/><Default Extension="tiff" ContentType="image/tiff"/><Default Extension="pdf" ContentType="application/pdf"/><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>PK
ÎæLJjùLL_rels/.rels<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>PK
ÎæLÐ?dÝ--xl/_rels/workbook.xml.rels<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>PK
ÎæL֒|ZZdocProps/core.xmlPK
ÎæLþù«44‰docProps/app.xmlPK
ÎæLT…Ä8ããëxl/worksheets/sheet1.xmlPK
ÎæLÜè¯ÏDDxl/workbook.xmlPK
ÎæL0ˆkÞÞ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中的一大坨字符串,通过 Uine8Array转成了无符号数组,并通过new Blob方法,转成了二进制文件片段,关键代码如下:

function blobify(strData) {
    var buf = new ArrayBuffer(strData.length), view = new Uint8Array(buf);
    for (var i=0; i!=strData.length; ++i) view[i] = strData.charCodeAt(i) & 0xFF;
    return buf;
}
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 (不晓得是不是可以导出音视频...)

猜你喜欢

转载自www.cnblogs.com/youryida/p/9275615.html