小石頭
          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

          ??? 前些天做了一個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.aygfsteel.com/wujiaqian/archive/2006/12/11/86970.html

          posted on 2007-01-08 15:38 小石頭 閱讀(1824) 評論(1)  編輯  收藏 所屬分類: 轉載區我的java學習

          FeedBack:
          # re: 通過構造XML數據流下載成Excel文件[轉]
          2012-08-02 12:56 | 隨便你叫
          這真心要感謝微軟大大們的努力以及開放出這個東西!  回復  更多評論
            
          主站蜘蛛池模板: 渑池县| 静海县| 台州市| 英超| 鲁甸县| 柳州市| 社会| 新宁县| 达拉特旗| 铜梁县| 临夏市| 奈曼旗| 涡阳县| 隆安县| 潍坊市| 平顶山市| 苗栗市| 海原县| 平谷区| 和静县| 思茅市| 东阿县| 南京市| 辽宁省| 潜江市| 固镇县| 洮南市| 都江堰市| 遂宁市| 东至县| 崇文区| 电白县| 永年县| 东明县| 班戈县| 扎鲁特旗| 临沭县| 乌鲁木齐县| 隆安县| 镇雄县| 兴安盟|