前些天做了一个EXCEL数据下载的东西,发现当数据超过10万行之后,就会内存溢出(用的是本机TOMCAT测试,内存有限,没能调过),新做一种方式,来感觉一下,发现速度有点慢,其他还可以.
一、问题描述
该问题出现是因为在导出文件之后
用户下载的是
.csv
文件,如果用文本编辑器打开可以查看所有记录,但是如果用
excel
打开就出现一个
sheet
最多
6
万条的记录。因此就不可以使用保存为
csv
文件来实现
excel
文件的下载,需要使用新的方式去实现。
如果使用
jxl
开发包在
web
后台去创建
Excel
文件,如果数据量比较大,则用户需要等待很长很长的时间才可以下载到,因为
jxl
的对于
excel
文件的操作都是对象级的
,
文件中每一个格子都是一个
cell
对象,需要后台去
new
。所以还需要考虑别的方式。
二、实现灵感
Excel
文件打开之后选择另存为可以保存为
XML
类型文件,因此就考虑构造符合
Excel
可以打开的
XML
类型文件,并且对该
XML
文件进行最简单化处理,去除
Excel
文件中的每个
cell
的
style
定义、
XML
文件头部的多余信息,最后整理出一个符合资讯平台所下载的
Excel
文件的格式
,
请看下面:
<!—XML
文件头部
--//>
<?xml version="1.0"?>
<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">
<!—Excel
文件第一个
SHEET --//>
<Worksheet ss:Name="sheet0">
<Table>
<Row>
<Cell><Data ss:Type="String">aa0</Data></Cell>
<Cell><Data ss:Type="String">aa1</Data></Cell>
<Cell><Data ss:Type="String">aa2</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">aa0</Data></Cell>
<Cell><Data ss:Type="String">aa1</Data></Cell>
<Cell><Data ss:Type="String">aa2</Data></Cell>
</Row>
</Table>
</Worksheet>
<!—Excel
文件第二个
SHEET --//>
<Worksheet ss:Name="sheet1">
<Table>
<!—
一行数据
--//>
<Row>
<Cell><Data ss:Type="String">aa0</Data></Cell>
<Cell><Data ss:Type="String">aa1</Data></Cell>
<Cell><Data ss:Type="String">aa2</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">aa0</Data></Cell>
<Cell><Data ss:Type="String">aa1</Data></Cell>
<Cell><Data ss:Type="String">aa2</Data></Cell>
</Row>
</Table>
</Worksheet>
</Workbook>
<!-- XML
文件结束
--//>
|
注释:
a
、
<Worksheet ss:Name="sheet0">
引号内部的是该
sheet
的名称。
b
、
<Data ss:Type="String">aa1</Data> ss:Type
的值是用来定义该
cell
格数据的类型,例如可以为
Number,
表是该
cell
格数据是数字。
三、实现方式
通过第一步的分析可以发现只要我们构建这样格式的
XML
数据,就可以通过
Excel
打开,并且可以实现分
sheet
的样式。但是数据下载到用户本地是
XML
类型的话,那是没什么意义的,就算打开方式选择使用
Excel
可以打开,因此如何将用户下载的文件类型改为
XLS
呢?这里就可以通过在下载的
servlet
中设置
response.setContentType("application/vnd.ms-excel")
来实现。
我实现了五个类来封闭XML字符串(CellData,TableCell,TableRow,WorkBook,WorkSheet),写了一个测试类
package com.hoten.util.xmlxls;
import java.util.List;
import java.util.ArrayList;
public class WorkBook {
private final static String XML_HEARDER = "<?xml version=\"1.0\"?>";
private List sheetList = new ArrayList(); //存放每行多个sheet的list
/**
* 取得workbook的xml文件的头部字符串
* @return
*/
private String getHeader(){
return XML_HEARDER +
"<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"" + Contants.SEP_N +
" xmlns:o=\"urn:schemas-microsoft-com:office:office\"" + Contants.SEP_N +
" xmlns:x=\"urn:schemas-microsoft-com:office:excel\"" + Contants.SEP_N +
" xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\"" + Contants.SEP_N +
" xmlns:html=\"http://www.w3.org/TR/REC-html40\">" + Contants.SEP_N ;
}
private String getFoot(){
return "</Workbook>";
}
public String toString(){
StringBuffer strBuff = new StringBuffer();
strBuff.append(getHeader());
int len = sheetList.size();
for(int i=0;i<len;i++){
WorkSheet sheet = (WorkSheet)sheetList.remove(0);
strBuff.append(sheet.toString());
sheet = null;
}
sheetList.clear();
strBuff.append(getFoot());
return strBuff.toString();
}
public void addSheet(WorkSheet sheet){
sheetList.add(sheet);
}
public void removeSheet(int i){
sheetList.remove(i);
}
}
package com.hoten.util.xmlxls;
import java.util.List;
import java.util.ArrayList;
public class WorkSheet {
private String name = ""; //该sheet的name
private List rowList = new ArrayList(); //存放每行多个row的list
public String toString(){
StringBuffer strBuff = new StringBuffer();
strBuff.append("<Worksheet ss:Name=\"" + name + "\">").append(Contants.SEP_N);
strBuff.append("<Table>").append(Contants.SEP_N);
int len = rowList.size();
for(int i=0;i<len;i++){
TableRow row = (TableRow)rowList.remove(0);
strBuff.append(row.toString());
row = null;
}
rowList.clear();
strBuff.append("</Table>").append(Contants.SEP_N);
strBuff.append("</Worksheet>").append(Contants.SEP_N);
return strBuff.toString();
}
public void addRow(TableRow row){
rowList.add(row);
}
public void removeRow(int i){
rowList.remove(i);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.hoten.util.xmlxls;
import java.util.List;
import java.util.ArrayList;
public class TableRow {
private List cellList = new ArrayList(); //存放每行多个cell的list
public String toString(){
StringBuffer strBuff = new StringBuffer();
strBuff.append("<Row>").append(Contants.SEP_N);
//循环显示每行的cell
int len = cellList.size();
for(int i=0;i<len;i++){
TableCell cell = (TableCell)cellList.remove(0);
strBuff.append(cell.toString()).append(Contants.SEP_N);
cell = null;
}
cellList.clear();
strBuff.append("</Row>").append(Contants.SEP_N);
return strBuff.toString();
}
public void addCell(TableCell cell){
cellList.add(cell);
}
public void removeCell(int i){
cellList.remove(i);
}
}
package com.hoten.util.xmlxls;
public class TableCell {
private String index = ""; //cell在每行显示的索引位置,可以不填
private CellData data = new CellData(); //cell的数据对象
public CellData getData() {
return data;
}
public void setData(CellData data) {
this.data = data;
}
public String getIndex() {
return index;
}
public void setIndex(String index) {
this.index = index;
}
public String toString(){
return "<Cell>" + data.toString() + "</Cell>";
}
}
package com.hoten.util.xmlxls;
public class CellData {
private String type = "String"; //cell数据类型
private String value = ""; //cell数据
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String toString(){
return "<Data ss:Type=\"" + type + "\">" + value + "</Data>";
}
}
package com.hoten.util.xmlxls;
public class Contants {
public final static String SEP_N = "\n";
/**
* XML中常量定义
*/
public final static String SS_NAME = "ss:Name";
public final static String SS_INDEX = "ss:Index";
public final static String SS_TYPE = "ss:Type";
}
测试的方法:
PrintWriter out = response.getWriter();
// 设置响应头和下载保存的文件名
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition","attachment; filename=\""+Chinese.toPage(fileNametemp)+"\"");
String rows_temp = request.getParameter("rows");//页面传过来的每个SHEET可以存放的条数
if (rows_temp == null){
rows_temp = "20000";
}
int count_rows = Integer.parseInt(rows_temp);//一个SHEET有多行
WorkBook book = new WorkBook();
Vector v_Rs = RsToVector.ResultSetToVector(rs);// 把RS的值转换成VECTOR,这个可以看我上一版本,上面有详细的写法
if (v_Rs != null) {
int a = v_Rs.size();
int sheet_count = a / count_rows;// 30000行一个sheet。
if (sheet_count >0){//大于0,则需要多个SHEET
for (int i = 0;i<sheet_count;i++){
WorkSheet sheet = new WorkSheet();//创建一个新的SHEET
sheet.setName("sheet" + i);//设置SHEET的名称
for (int b = i* count_rows ;b<(i+1) * count_rows ;b++){
//temp_v.add(v_Rs.get(b));
Vector temp_v = new Vector();
temp_v =(Vector) v_Rs.get(b);
//取出一个对象,把对象的值放到EXCEL里
TableRow row = new TableRow();//设置行
for(int m=0;m<numColumns;m++){
TableCell cell = new TableCell();
CellData data = new CellData();
data.setValue((String)temp_v.get(m));
cell.setData(data);
row.addCell(cell);
}
sheet.addRow(row);
}
book.addSheet(sheet);
}
//还有剩余的数据
if (sheet_count * count_rows < a){
WorkSheet sheet = new WorkSheet();//创建一个新的SHEET
sheet.setName("sheet" + sheet_count);//设置SHEET的名称
for (int c = sheet_count* count_rows ;c<a ;c++){
Vector temp_vv = new Vector();
temp_vv =(Vector) v_Rs.get(c);
//取出一个对象,把对象的值放到EXCEL里
TableRow row = new TableRow();//设置行
for(int m=0;m<numColumns;m++){
TableCell cell = new TableCell();
CellData data = new CellData();
data.setValue((String)temp_vv.get(m));
cell.setData(data);
row.addCell(cell);
}
sheet.addRow(row);
}
book.addSheet(sheet);
}
}else{
WorkSheet sheet = new WorkSheet();//创建一个新的SHEET
sheet.setName("sheet1");//设置SHEET的名称
for (int bb=0 ;bb<a ;bb++){
//temp_v.add(v_Rs.get(b));
Vector temp_v = new Vector();
temp_v =(Vector) v_Rs.get(bb);
//取出一个对象,把对象的值放到EXCEL里
TableRow row = new TableRow();//设置行
for(int m=0;m<numColumns;m++){
TableCell cell = new TableCell();
CellData data = new CellData();
data.setValue((String)temp_v.get(m));
cell.setData(data);
row.addCell(cell);
}
sheet.addRow(row);
}
book.addSheet(sheet);
}
out.print(book.toString());
}
以上拼XML的时候重复代码比较多,可以写一个公用的方法,,我为了把XML描述的详细一点,把这些都封装成了对象,但在拼字符串的时候,对象就会太多,以后如果改版的话,可以把它尽量封装少一点对象,这样速度可能会快一点,内存可能会少用一点.
转 : http://www.blogjava.net/wujiaqian/archive/2006/12/11/86970.html
posted on 2007-01-08 15:38
小石头 阅读(1804)
评论(1) 编辑 收藏 所属分类:
转载区 、
我的java学习