今天遇到了一个POI导出EXCEL文件结果文件大小太大导致系统运行缓慢的问题。想到了使用输出XML格式的表格来完成。
首先,我们需要理解一下为什么POI导出EXCEL文件会导致文件大小变大。最主要的原因应该是POI变成中经常会对无关的单元格设置属性。例如:一个EXCEL工作表的最大列数为256,我们只使用10列。而编程时经常会将256列全部渲染格式,造成很大的资源浪费。当然应该还有其他很多原因也会造成表格变大。
使用这个方案的前提是对于表格的格式要求不高,因为使用的是自己的工具,没有POI优化了那么多格式。
接下来我们看一下这个方法的理论依据:
我们新建一个aa.xls。内容就少一点,方便观察。内容如下:
使用文件--》另存为--》.xml 得到一个 aa.xml。
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Created>1996-12-17T01:32:42Z</Created>
<LastSaved>2015-03-04T02:45:33Z</LastSaved>
<Version>11.9999</Version>
</DocumentProperties>
<OfficeDocumentSettings xmlns="urn:schemas-microsoft-com:office:office">
<RemovePersonalInformation/>
</OfficeDocumentSettings>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>4530</WindowHeight>
<WindowWidth>8505</WindowWidth>
<WindowTopX>480</WindowTopX>
<WindowTopY>120</WindowTopY>
<AcceptLabelsInFormulas/>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
<Borders/>
<Font ss:FontName="宋体" x:CharSet="134" ss:Size="12"/>
<Interior/>
<NumberFormat/>
<Protection/>
</Style>
<Style ss:ID="s21">
<Font ss:FontName="宋体" x:CharSet="134" ss:Size="12" ss:Bold="1"/>
</Style>
</Styles>
<Worksheet ss:Name="Sheet1">
<Table ss:ExpandedColumnCount="5" ss:ExpandedRowCount="12" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="14.25">
<Row>
<Cell ss:StyleID="s21"><Data ss:Type="String">A</Data></Cell>
<Cell ss:StyleID="s21"><Data ss:Type="String">B</Data></Cell>
<Cell ss:StyleID="s21"><Data ss:Type="String">C</Data></Cell>
<Cell ss:StyleID="s21"><Data ss:Type="String">D</Data></Cell>
<Cell ss:StyleID="s21"><Data ss:Type="String">E</Data></Cell>
</Row>
<Row ss:Index="4">
<Cell ss:Index="2"><Data ss:Type="String">aaa</Data></Cell>
</Row>
<Row ss:Index="8">
<Cell ss:Index="4"><Data ss:Type="String">ddd</Data></Cell>
</Row>
<Row>
<Cell ss:Index="2"><Data ss:Type="String">sss</Data></Cell>
</Row>
<Row ss:Index="11">
<Cell><Data ss:Type="String">fddd</Data></Cell>
</Row>
<Row>
<Cell ss:Index="4"><Data ss:Type="String">fff</Data></Cell>
</Row>
</Table>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<Print>
<ValidPrinterInfo/>
<PaperSizeIndex>9</PaperSizeIndex>
<HorizontalResolution>200</HorizontalResolution>
<VerticalResolution>200</VerticalResolution>
</Print>
<Selected/>
<Panes>
<Pane>
<Number>3</Number>
<RangeSelection>R1C1:R12C5</RangeSelection>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Sheet2">
<Table ss:ExpandedColumnCount="0" ss:ExpandedRowCount="0" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="14.25"/>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
<Worksheet ss:Name="Sheet3">
<Table ss:ExpandedColumnCount="0" ss:ExpandedRowCount="0" x:FullColumns="1"
x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="14.25"/>
<WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>
可以看到 其实输出文件就是一个标准的xml文档。而且该文档还有一个很特殊的地方就是:文档的后缀名一旦修改为.XLS那么直接就可以生成一个表格文件。所以,如果我们找到XML的对应关系就可以通过输出XML文件,重命名为.XLS文件。完成减小EXCEL大小的目的。
最后贴上一个测试的类 TEST.java 完成了基本对应关系。但是还有很多需要摸索的地方:例如格式,函数。希望对大家有所帮助。
1 package com.sun;
2 import java.io.BufferedWriter;
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.OutputStreamWriter;
8
9
10 public class Test {
11 public static void main(String[] args) {
12 StringBuffer sb = new StringBuffer();
13 try {
14 OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(new File("d://aa.xls")),"UTF-8");
15 BufferedWriter output = new BufferedWriter(write);
16 sb.append("<?xml version=\"1.0\"?>");
17 sb.append("\n");
18 sb.append("<?mso-application progid=\"Excel.Sheet\"?>");
19 sb.append("\n");
20 sb.append("<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"");
21 sb.append("\n");
22 sb.append(" xmlns:o=\"urn:schemas-microsoft-com:office:office\"");
23 sb.append("\n");
24 sb.append(" xmlns:x=\"urn:schemas-microsoft-com:office:excel\"");
25 sb.append("\n");
26 sb.append(" xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\"");
27 sb.append("\n");
28 sb.append(" xmlns:html=\"http://www.w3.org/TR/REC-html40\">");
29 sb.append("\n");
30 sb.append(" <Styles>\n");
31 sb.append(" <Style ss:ID=\"Default\" ss:Name=\"Normal\">\n");
32 sb.append(" <Alignment ss:Vertical=\"Center\"/>\n");
33 sb.append(" <Borders/>\n");
34 sb.append(" <Font ss:FontName=\"宋体\" x:CharSet=\"134\" ss:Size=\"12\"/>\n");
35 sb.append(" <Interior/>\n");
36 sb.append(" <NumberFormat/>\n");
37 sb.append(" <Protection/>\n");
38 sb.append(" </Style>\n");
39 sb.append(" </Styles>\n");
40 //行数
41 int rowNum = 20;
42
43 int currentRecord = 0;
44 //总数据量
45 int total = 2000;
46 //列数
47 int columnNum = 10;
48 //第一个工作表
49 sb.append("<Worksheet ss:Name=\"Sheet0\">");
50 sb.append("\n");
51 sb.append("<Table ss:ExpandedColumnCount=\"" + columnNum
52 + "\" ss:ExpandedRowCount=\"" + rowNum
53 + "\" x:FullColumns=\"1\" x:FullRows=\"1\">");
54 sb.append("\n");
55 for (int i = 0; i < total; i++) {
56 if ((currentRecord == rowNum
57 || currentRecord > rowNum || currentRecord == 0)
58 && i != 0) {// 一个sheet写满
59 currentRecord = 0;
60 output.write(sb.toString());
61 sb.setLength(0);
62 sb.append("</Table>");
63 sb.append("<WorksheetOptions xmlns=\"urn:schemas-microsoft-com:office:excel\">");
64 sb.append("\n");
65 sb.append("<ProtectObjects>False</ProtectObjects>");
66 sb.append("\n");
67 sb.append("<ProtectScenarios>False</ProtectScenarios>");
68 sb.append("\n");
69 sb.append("</WorksheetOptions>");
70 sb.append("\n");
71 sb.append("</Worksheet>");
72 sb.append("<Worksheet ss:Name=\"Sheet" + i / rowNum
73 + "\">");
74 sb.append("\n");
75 sb.append("<Table ss:ExpandedColumnCount=\"" + columnNum
76 + "\" ss:ExpandedRowCount=\"" + rowNum
77 + "\" x:FullColumns=\"1\" x:FullRows=\"1\">");
78 sb.append("\n");
79 }
80 sb.append("<Row>");
81 for (int j = 0; j < columnNum; j++) {
82 sb.append("<Cell><Data ss:Type=\"String\">第"+(i+1)+"列第"+(j+1)+"行</Data></Cell>");
83 sb.append("\n");
84 }
85 sb.append("</Row>");
86 //每三百行数据批量提交一次
87 if (i % 300 == 0) {
88 System.out.println("提交了");
89 output.write(sb.toString());
90 output.flush();
91 sb.setLength(0);
92 }
93 sb.append("\n");
94 currentRecord++;
95 }
96 output.write(sb.toString());
97 sb.setLength(0);
98 sb.append("</Table>");
99 sb.append("<WorksheetOptions xmlns=\"urn:schemas-microsoft-com:office:excel\">");
100 sb.append("\n");
101 sb.append("<ProtectObjects>False</ProtectObjects>");
102 sb.append("\n");
103 sb.append("<ProtectScenarios>False</ProtectScenarios>");
104 sb.append("\n");
105 sb.append("</WorksheetOptions>");
106 sb.append("\n");
107 sb.append("</Worksheet>");
108 sb.append("</Workbook>");
109 sb.append("\n");
110 output.write(sb.toString());
111 output.flush();
112 output.close();
113 } catch (FileNotFoundException e) {
114 e.printStackTrace();
115 } catch (IOException e) {
116 e.printStackTrace();
117 }
118 }
119 }