小石頭
          Excellence in any department can be attained only by the labor of a lifetime; it is not to be purchased at a lesser price.
          posts - 91,comments - 22,trackbacks - 0

          ??? 前些天做了一個(gè)EXCEL數(shù)據(jù)下載的東西,發(fā)現(xiàn)當(dāng)數(shù)據(jù)超過10萬行之后,就會(huì)內(nèi)存溢出(用的是本機(jī)TOMCAT測(cè)試,內(nèi)存有限,沒能調(diào)過),新做一種方式,來感覺一下,發(fā)現(xiàn)速度有點(diǎn)慢,其他還可以.?

          一、問題描述

          ??? 該問題出現(xiàn)是因?yàn)樵趯?dǎo)出文件之后 用戶下載的是 .csv 文件,如果用文本編輯器打開可以查看所有記錄,但是如果用 excel 打開就出現(xiàn)一個(gè) sheet 最多 6 萬條的記錄。因此就不可以使用保存為 csv 文件來實(shí)現(xiàn) excel 文件的下載,需要使用新的方式去實(shí)現(xiàn)。

          ??? 如果使用 jxl 開發(fā)包在 web 后臺(tái)去創(chuàng)建 Excel 文件,如果數(shù)據(jù)量比較大,則用戶需要等待很長(zhǎng)很長(zhǎng)的時(shí)間才可以下載到,因?yàn)?/span> jxl 的對(duì)于 excel 文件的操作都是對(duì)象級(jí)的 , 文件中每一個(gè)格子都是一個(gè) cell 對(duì)象,需要后臺(tái)去 new 。所以還需要考慮別的方式。

          ???

          二、實(shí)現(xiàn)靈感

          ??? Excel 文件打開之后選擇另存為可以保存為 XML 類型文件,因此就考慮構(gòu)造符合 Excel 可以打開的 XML 類型文件,并且對(duì)該 XML 文件進(jìn)行最簡(jiǎn)單化處理,去除 Excel 文件中的每個(gè) cell style 定義、 XML 文件頭部的多余信息,最后整理出一個(gè)符合資訊平臺(tái)所下載的 Excel 文件的格式 , 請(qǐng)看下面:

          ?

          ?

          <!—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 文件第一個(gè) 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 文件第二個(gè) SHEET --//>

          <Worksheet ss:Name="sheet1">

          <Table>

          ??? <!— 一行數(shù)據(jù) --//>

          <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 文件結(jié)束 --//>

          ?

          ?

          注釋:

          a <Worksheet ss:Name="sheet0">? 引號(hào)內(nèi)部的是該 sheet 的名稱。

          b <Data ss:Type="String">aa1</Data> ?ss:Type 的值是用來定義該 cell 格數(shù)據(jù)的類型,例如可以為 Number, 表是該 cell 格數(shù)據(jù)是數(shù)字。

          ?

          ?

          三、實(shí)現(xiàn)方式

          ??? 通過第一步的分析可以發(fā)現(xiàn)只要我們構(gòu)建這樣格式的 XML 數(shù)據(jù),就可以通過 Excel 打開,并且可以實(shí)現(xiàn)分 sheet 的樣式。但是數(shù)據(jù)下載到用戶本地是 XML 類型的話,那是沒什么意義的,就算打開方式選擇使用 Excel 可以打開,因此如何將用戶下載的文件類型改為 XLS 呢?這里就可以通過在下載的 servlet 中設(shè)置 response.setContentType("application/vnd.ms-excel") 來實(shí)現(xiàn)。
          我實(shí)現(xiàn)了五個(gè)類來封閉XML字符串(CellData,TableCell,TableRow,WorkBook,WorkSheet),寫了一個(gè)測(cè)試類

          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(); //存放每行多個(gè)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(); //存放每行多個(gè)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(); //存放每行多個(gè)cell的list
          ?
          ?
          ?public String toString(){
          ??StringBuffer strBuff = new StringBuffer();
          ??
          ??strBuff.append("<Row>").append(Contants.SEP_N);

          ??//循環(huá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的數(shù)據(jù)對(duì)象

          ?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數(shù)據(jù)類型
          ?private String value = ""; //cell數(shù)據(jù)
          ?
          ?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";

          }



          測(cè)試的方法:
          PrintWriter out = response.getWriter();
          ???????? // 設(shè)置響應(yīng)頭和下載保存的文件名
          ???????? response.setContentType("application/vnd.ms-excel");
          ???????? response.setHeader("Content-Disposition","attachment; filename=\""+Chinese.toPage(fileNametemp)+"\"");
          ????????
          ???????? String rows_temp = request.getParameter("rows");//頁(yè)面?zhèn)鬟^來的每個(gè)SHEET可以存放的條數(shù)
          ???if (rows_temp == null){
          ????rows_temp = "20000";
          ???}
          ???int count_rows = Integer.parseInt(rows_temp);//一個(gè)SHEET有多行
          ????????
          ???????? WorkBook book = new WorkBook();
          ???????? Vector v_Rs = RsToVector.ResultSetToVector(rs);// 把RS的值轉(zhuǎn)換成VECTOR,這個(gè)可以看我上一版本,上面有詳細(xì)的寫法
          ?????????
          ???????? if (v_Rs != null) {
          ????int a = v_Rs.size();????
          ????int sheet_count = a / count_rows;// 30000行一個(gè)sheet。
          ????
          ????if (sheet_count >0){//大于0,則需要多個(gè)SHEET
          ?????for (int i = 0;i<sheet_count;i++){
          ??????WorkSheet sheet = new WorkSheet();//創(chuàng)建一個(gè)新的SHEET
          ??????sheet.setName("sheet" + i);//設(shè)置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);???????
          ???????//取出一個(gè)對(duì)象,把對(duì)象的值放到EXCEL里
          ???????TableRow row = new TableRow();//設(shè)置行?
          ???????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);
          ?????}
          ?????//還有剩余的數(shù)據(jù)
          ?????if (sheet_count * count_rows < a){
          ??????WorkSheet sheet = new WorkSheet();//創(chuàng)建一個(gè)新的SHEET
          ??????sheet.setName("sheet" + sheet_count);//設(shè)置SHEET的名稱
          ???????
          ??????for (int c = sheet_count* count_rows ;c<a ;c++){???????
          ???????Vector temp_vv = new Vector();
          ???????temp_vv =(Vector) v_Rs.get(c);
          ???????//取出一個(gè)對(duì)象,把對(duì)象的值放到EXCEL里
          ???????TableRow row = new TableRow();//設(shè)置行
          ???????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();//創(chuàng)建一個(gè)新的SHEET
          ??????sheet.setName("sheet1");//設(shè)置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);???????
          ???????//取出一個(gè)對(duì)象,把對(duì)象的值放到EXCEL里
          ???????TableRow row = new TableRow();//設(shè)置行?
          ???????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的時(shí)候重復(fù)代碼比較多,可以寫一個(gè)公用的方法,,我為了把XML描述的詳細(xì)一點(diǎn),把這些都封裝成了對(duì)象,但在拼字符串的時(shí)候,對(duì)象就會(huì)太多,以后如果改版的話,可以把它盡量封裝少一點(diǎn)對(duì)象,這樣速度可能會(huì)快一點(diǎn),內(nèi)存可能會(huì)少用一點(diǎn).

          ?

          轉(zhuǎn) : http://www.aygfsteel.com/wujiaqian/archive/2006/12/11/86970.html

          posted on 2007-01-08 15:38 小石頭 閱讀(1831) 評(píng)論(1)  編輯  收藏 所屬分類: 轉(zhuǎn)載區(qū)我的java學(xué)習(xí)

          FeedBack:
          # re: 通過構(gòu)造XML數(shù)據(jù)流下載成Excel文件[轉(zhuǎn)]
          2012-08-02 12:56 | 隨便你叫
          這真心要感謝微軟大大們的努力以及開放出這個(gè)東西!  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 普陀区| 镇赉县| 家居| 翁源县| 南靖县| 商都县| 新龙县| 沙河市| 本溪市| 金华市| 恩施市| 柘城县| 容城县| 黔南| 长兴县| 武冈市| 雷州市| 太康县| 桦川县| 白山市| 新疆| 凤山县| 广东省| 南漳县| 张家港市| 四平市| 车致| 余干县| 泽普县| 乐山市| 赣榆县| 曲水县| 大同县| 龙山县| 诏安县| 潢川县| 宜章县| 乐东| 衡水市| 大渡口区| 安康市|