前阵子跟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不行。
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 ÃæL0kÞÞ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="ï¼ï¼³ ï¼°ã´ã·ãã¯"/><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="ï¼ï¼³ ï¼°ã´ã·ãã¯"/><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=""ä¸å/ä¸å "hh"æ"mm"å"ss"ç§ ""/></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þù«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中的一大坨字符串通过 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 (不晓得是不是可以导出音视频...)
转载请注明出处http://www.cnblogs.com/youryida
热门评论
完全懵逼。是因为我没了解文件转换的原理吗?简单的excel导出在这里看了大大的解释,我理解为:html页面右键菜单里有个另存为的功能,可以把这个html页面下载保存到本地,JavaScript代码通过blob或base64调用了这个功能,并且选择保存格式为xls,然后我们只用点击有这个JavaScript代码的按钮,就可以省去右键另存为xls的步骤,一键下载这个html然后保存为xls格式。不知道这个理解正确吗?第二个就完全看不懂了,大大可以推一个助于理解文件下载上传详细转换原理的资料吗?