告別ASP.NET操作EXCEL的煩惱

ASP.NET操作EXCEL代碼

公元19XX年前,關於EXCEL的操作就如滔滔江水,連綿不絕,真正操作EXCEL我也是從去年下半年開始的,有些比較複雜的年度報表之類的,做起來也有點費力,不過還是都能畫出來了,關於EXCEL的報表導出,考慮到導出耗時的問題我主要採用AJAX來做的,分別捕捉幾個起止狀態,給客戶端提示3個狀態:正在檢索數據。。。---》準備導出數據。。。(只是從數據庫成功取出,還沒有讀寫excel文件)--》正在讀寫文件--》導出數據成功,當然如果哪一過程出錯,都有對應的提示,只所以想到寫這篇文章,主要是因爲今年有個系統的部分EXCEL的操作也讓我做,順便結合之前操作EXCEL的經驗作一下總結,可能也算不上什麼,對於絕大多數來說也沒什麼技術含量,網上一搜一大把,但我想還是有必要總結一下,至少能給園子裏的新手些許幫助,OK,Let's Go...

   一. 程序操作EXCEL的應用主要還是在統計報表方面,您可能會考慮讀EXCEL模板,也可能會考慮沒必要讀模板,其實讀不讀模板都能達到一樣的效果,看實際情況而用了。
       1. 讀模板的話,首先模板存放在某個路徑下,根據模板把從數據庫裏取出的數據寫回EXCEL然後生成一個新的EXCEL存放都另一個路徑以供下載,模板不變。
          我這裏的EXCEL操作主要是在VS2005裏的,VS2003也可以的,不過沒怎麼研究03裏的操作(文章最後我會把05的示例下載地址貼上那個demo裏之前打包忘了放了一個生成數據的文件,剛放進去了,不加也是可以運行的,還有模板文件的數據稍微過濾了下重新放了部分對照看下)vs05中操作EXCEL直接引用.NET自帶的COM組件,添加後項目的bin目錄下會自動出現
      
Interop.Excel.dll這個DLL(需安裝office2003 excel,下面的說明及示例都是基於office2003的,版本不同調用可能會不一樣)
頁面的命名空間引用 using Excel;
下面是調用模板的一段代碼

 1  #region 使用模板導出Excel表
 2                 case "ReportByTemp":
 3                     {
 4 
 5                         DataView dv = Cache["ReportByTemp"as DataView;
 6                         //建立一個Excel.Application的新進程
 7                         Excel.Application app = new Excel.Application();
 8                         if (app == null)
 9                         {
10                             return;
11                         }
12                         app.Visible = false;
13                         app.UserControl = true;
14                         Workbooks workbooks = app.Workbooks;
15                         _Workbook workbook = workbooks.Add(template_path + "//EXCEL測試模板.xls");//這裏的Add方法裏的參數就是模板的路徑
16                         Sheets sheets = workbook.Worksheets;
17                         _Worksheet worksheet = (_Worksheet)sheets.get_Item(1);//模板只有一個sheet表
18                         if (worksheet == null)
19                         {
20                             return;
21                         }
22 
23                         int rowNum = 0;
24                         for (int i = 0; i < dv.Count; i++)
25                         {
26                             rowNum = i + 1;
27                             worksheet.Cells[3 + i, 1= rowNum;
28                             worksheet.Cells[3 + i, 2= dv[i].Row[0].ToString();
29                             worksheet.Cells[3 + i, 3= dv[i].Row[1].ToString();
30 
31                             excelOperate.SetBold(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 1]); //黑體
32                             excelOperate.SetHAlignCenter(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]);//居中
33                             worksheet.get_Range(worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
34 
35                         }
36 
37                         tick = DateTime.Now.Ticks.ToString();
38                         save_path = temp_path + "//" + tick + ".xls";
39                         workbook.SaveAs(save_path, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
40                         excelOperate.Dispose(worksheet, workbook, app);//關閉Excel進程
41 
42                     }
43                     break;
44                 #endregion

效果如下:

       
       2. 不讀模板的話,調用的時候其實會繼承一個空白模板,然後寫入數據,程序畫表頭,最終達到一樣的效果,程序如下:

 1  #region 不使用模板生成Excel表
 2                 case "ReportByNone":
 3                     {
 4 
 5                         DataView dv = Cache["ReportByNone"as DataView;
 6                         //建立一個Excel.Application的新進程
 7                         Excel.Application app = new Excel.Application();
 8                         if (app == null)
 9                         {
10                             return;
11                         }
12                         app.Visible = false;
13                         app.UserControl = true;
14                         Workbooks workbooks = app.Workbooks;
15                         _Workbook workbook = workbooks.Add(XlWBATemplate.xlWBATWorksheet);//這裏的Add方法裏的參數就相當於繼承了一個空模板(暫這樣理解吧)
16                         Sheets sheets = workbook.Worksheets;
17                         _Worksheet worksheet = (_Worksheet)sheets.get_Item(1);
18                         if (worksheet == null)
19                         {
20                             return;
21                         }
22 
23                         worksheet.get_Range(worksheet.Cells[11], worksheet.Cells[13]).Merge(Missing.Value); //橫向合併
24                         worksheet.get_Range(worksheet.Cells[11], worksheet.Cells[11]).Value2 = "導出EXCEL測試一";
25                         excelOperate.SetBold(worksheet, worksheet.Cells[11], worksheet.Cells[11]); //黑體
26                         excelOperate.SetHAlignCenter(worksheet, worksheet.Cells[11], worksheet.Cells[11]);//居中
27                         excelOperate.SetBgColor(worksheet, worksheet.Cells[11], worksheet.Cells[11], System.Drawing.Color.Red);//背景色
28                         excelOperate.SetFontSize(worksheet, worksheet.Cells[11], worksheet.Cells[11], 16);//字體大小
29                         excelOperate.SetRowHeight(worksheet, worksheet.Cells[11], worksheet.Cells[11], 32.25);//行高
30                         worksheet.get_Range(worksheet.Cells[11], worksheet.Cells[11]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);//黑色連續邊框
31 
32                         worksheet.Cells[21= "序號";
33                         worksheet.Cells[22= "公司";
34                         worksheet.Cells[23= "部門";
35                         excelOperate.SetBold(worksheet, worksheet.Cells[21], worksheet.Cells[23]); //黑體
36                         worksheet.get_Range(worksheet.Cells[21], worksheet.Cells[23]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
37                         excelOperate.SetHAlignRight(worksheet, worksheet.Cells[21], worksheet.Cells[23]);
38                         excelOperate.SetBgColor(worksheet, worksheet.Cells[21], worksheet.Cells[23], System.Drawing.Color.Silver);//背景色
39                         int rowNum = 0;
40                         for (int i = 0; i < dv.Count; i++)
41                         {
42                             rowNum = i + 1;
43                             worksheet.Cells[3 + i, 1= rowNum;
44                             worksheet.Cells[3 + i, 2= dv[i].Row[0].ToString();
45                             worksheet.Cells[3 + i, 3= dv[i].Row[1].ToString();
46 
47                             excelOperate.SetBold(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 1]); //黑體
48                             excelOperate.SetHAlignCenter(worksheet, worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]);//居中
49                             worksheet.get_Range(worksheet.Cells[3 + i, 1], worksheet.Cells[3 + i, 3]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);//設置邊框顏色,不然打印預覽,會非常不雅觀
50 
51                         }
52                         excelOperate.SetColumnWidth(worksheet, "A"10);
53                         excelOperate.SetColumnWidth(worksheet, "B"20);
54                         excelOperate.SetColumnWidth(worksheet, "C"20);
55                         worksheet.Name = "導出EXCEL測試一";
56 
57                         tick = DateTime.Now.Ticks.ToString();
58                         save_path = temp_path + "//"+ tick + ".xls";
59                         workbook.SaveAs(save_path, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
60                         excelOperate.Dispose(worksheet, workbook, app);//關閉Excel進程
61 
62                     }
63                     break;
64 
65                 #endregion

效果如下:


以上我給了兩個最簡單的操作說明,下面詳細說一下對於一些稍微複雜的報表的生成處理

      二. 對於複雜的EXCEL報表的生成處理,無非是縱向合併相同的數據行及嵌套縱向合併等一些操作,下面就幾個具有針對性的報表作下說明.
            1.要生成相對複雜的EXCEL表,在從數據庫取數據時,要注意先按照合理的要求排好序,有時候可能order by後面要跟好幾個字段,而且這幾個字段誰先誰後也要注意,因爲這些會直接影響報表呈現的效果,比如你的EXCEL表要按月份統計國內外的項目,顯示出來的時候要多個項目相同的人連續,那麼排序就可能要這樣order by 月份,項目類別,用戶ID,項目ID(這是寫好的視圖,基於視圖來檢索的),這個排序的字段順序就不能變了,變了的話就不太好生成想要的形式了,如下圖:

這個也是動態畫的,用了個簡單的模板,模板就一個表頭,沒多大意義,除非表頭很複雜而且在列表中不需要重畫,考慮模板就比較好,向上面那個一月份國際的和其它月份的都是需要重畫表頭的。至於合併,如果不是嵌套的合併,我們可以在向模板循環寫數據的時候直接控制,比如下面一個簡單的寫法:

 1  for (i = 0; i < table.Rows.Count; i++)
 2                             {
 3                                 bidName = table.Rows[index]["BIDNAME"].ToString();
 4                                 if (table.Rows[i]["BIDNAME"].ToString() == bidName)
 5                                 {
 6                                     projNum++;
 7                                     worksheet.Cells[5 + i, 2= table.Rows[i]["PROJNO"];
 8                                     worksheet.Cells[5 + i, 3= table.Rows[i]["PROJNAME"];
 9                                     worksheet.Cells[5 + i, 4= table.Rows[i]["STAT_DATE"];
10                                     worksheet.Cells[5 + i, 5= table.Rows[i]["PROJTYPE"];
11                                     worksheet.Cells[5 + i, 6= table.Rows[i]["CONTENT"];
12                                     worksheet.Cells[5 + i, 7= table.Rows[i]["OPENDT"];
13                                     worksheet.Cells[5 + i, 8= table.Rows[i]["OPENADDRESS"];
14                                     worksheet.Cells[5 + i, 9= table.Rows[i]["REV_DATE"];
15                                     worksheet.Cells[5 + i, 10= table.Rows[i]["BID_UNIT"];
16                                     worksheet.Cells[5 + i, 11= table.Rows[i]["AGT_AMOUNT"];
17                                     worksheet.Cells[5 + i, 12= table.Rows[i]["CURRENCY"+ ":" + table.Rows[i]["BIDSER_AMOUNT"];
18                                     worksheet.Cells[5 + i, 13= table.Rows[i]["SENDDATE"];
19                                     worksheet.Cells[5 + i, 14= table.Rows[i]["CURRENCY"+ ":" + table.Rows[i]["BIDPRICE"];
20                                     worksheet.Cells[5 + i, 15= table.Rows[i]["BOOKAMOUNT"];
21                                     worksheet.Cells[5 + i, 16= table.Rows[i]["CURRENCY"+ ":" + table.Rows[i]["BAIL_AMOUNT"];
22                                     worksheet.Cells[5 + i, 17= table.Rows[i]["USERNAME"];
23                                     worksheet.Cells[5 + i, 18= table.Rows[i]["SECOND_USER"];
24                                     worksheet.Cells[5 + i, 19= "";
25                                     worksheet.get_Range(worksheet.Cells[5 + i, 1], worksheet.Cells[5 + i, 19]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
26                                     continue;
27                                 }
28 
29                                 worksheet.get_Range(worksheet.Cells[5 + rowid, 1], worksheet.Cells[5 + i - 11]).Merge(Missing.Value); //將第一列按投標單位合併
30                                 worksheet.get_Range(worksheet.Cells[5 + rowid, 1], worksheet.Cells[5 + rowid, 1]).Value2 = bidName + "(" + projNum.ToString() + "個項目)";//合併後的單元格內容

合併單元格的時候也要注意一個問題,就是合併的單元格必須是爲空的,不然在執行合併時,會提示“合併後的單元格的值將丟失”,具體不這樣提示的,大致是這個意思,一般我們合併都單元格相同的內容,在合併前我們先保存那個值,再清空後合併,上面的代碼中把worksheet.Cell[5+rowid,1]這裏系列的單元格的值空出來了,沒寫數據,而且最後合併了再寫值,避免了去循環清空。
     2.嵌套的合併向上面那樣做可能控制比較麻煩,而且思路可能很混亂,我們可以考慮先循環填充所有的數據,在循環出來要合併的列,比如像下面的這張表

先循環填充數據,如下:

 1  int index = 0, rownum = 0;
 2                             string ProjNo = "";
 3                             for (i = 0; i < table.Rows.Count; i++)
 4                             {
 5                                 ProjNo = table.Rows[index]["PROJNO"].ToString();
 6                                 if (table.Rows[i]["PROJNO"].ToString() == ProjNo)
 7                                 {
 8                                     wksheet.Cells[3 + i, 1= rownum + 1;
 9                                     wksheet.Cells[3 + i, 2= "'" + table.Rows[i]["PROJNO"];   //加上單引號保證以0開頭的字符原樣輸出
10                                     wksheet.Cells[3 + i, 3= "'" + table.Rows[i]["PROJNAME"];
11                                     wksheet.Cells[3 + i, 4= "'" + table.Rows[i]["PA_NAME"];
12                                     wksheet.Cells[3 + i, 5= "'" + table.Rows[i]["BIDER_NAME"];
13                                     wksheet.Cells[3 + i, 6= table.Rows[i]["BAIL_AMOUNT"];
14                                     wksheet.Cells[3 + i, 7= table.Rows[i]["NOT_BACK"];
15                                     wksheet.get_Range(wksheet.Cells[3 + i, 1], wksheet.Cells[3 + i, 7]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
16                                     continue;
17                                 }
18 
19                                 index = i;
20                                 rownum++;
21                                 i--;
22 
23                             }

下面合併前三列相同內容的單元:

 1  //合併前三列操作
 2                             int m = 1, rowid = 3, k;
 3                             string projName = "";
 4                             for (k = 3; k <= i + 2; k++)
 5                             {
 6                                 if (Convert.ToInt32(wksheet.get_Range(wksheet.Cells[k, 1], wksheet.Cells[k, 1]).Value2) == m)
 7                                 {
 8                                     ProjNo = wksheet.get_Range(wksheet.Cells[k, 2], wksheet.Cells[k, 2]).Value2.ToString();
 9                                     projName = wksheet.get_Range(wksheet.Cells[k, 3], wksheet.Cells[k, 3]).Value2.ToString();
10                                     wksheet.get_Range(wksheet.Cells[k, 1], wksheet.Cells[k, 1]).Value2 = "";
11                                     wksheet.get_Range(wksheet.Cells[k, 2], wksheet.Cells[k, 2]).Value2 = "";
12                                     wksheet.get_Range(wksheet.Cells[k, 3], wksheet.Cells[k, 3]).Value2 = "";
13                                     continue;
14                                 }
15                                 wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[k - 11]).Merge(Missing.Value);
16                                 wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[rowid, 1]).Value2 = m;
17 
18                                 wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[k - 12]).Merge(Missing.Value);
19                                 wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[rowid, 2]).Value2 = "'" + ProjNo;
20 
21                                 wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[k - 13]).Merge(Missing.Value);
22                                 wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[rowid, 3]).Value2 = "'" + projName;
23 
24                                 m++;
25                                 rowid = k;
26                                 k--;
27                             }
28                             //跳出循環後合併最後一個招標項目
29 
30                             wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[k - 11]).Merge(Missing.Value);
31                             wksheet.get_Range(wksheet.Cells[rowid, 1], wksheet.Cells[rowid, 1]).Value2 = m;
32 
33                             wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[k - 12]).Merge(Missing.Value);
34                             wksheet.get_Range(wksheet.Cells[rowid, 2], wksheet.Cells[rowid, 2]).Value2 = "'" + ProjNo;
35 
36                             wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[k - 13]).Merge(Missing.Value);
37                             wksheet.get_Range(wksheet.Cells[rowid, 3], wksheet.Cells[rowid, 3]).Value2 = "'" + projName;

下面合併標段列

 1  //合併標段列
 2 
 3                             index = 0; rowid = 3//重置變量
 4                             string pa_name = string.Empty; //標段名稱
 5                             for (k = 3; k <= i + 2; k++)
 6                             {
 7                                 pa_name = table.Rows[index]["PA_NAME"].ToString();
 8                                 if (wksheet.get_Range(wksheet.Cells[k, 4], wksheet.Cells[k, 4]).Value2.ToString() == pa_name)
 9                                 {
10                                     wksheet.get_Range(wksheet.Cells[k, 4], wksheet.Cells[k, 4]).Value2 = "";
11                                     continue;
12                                 }
13                                 wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[k - 14]).Merge(Missing.Value);
14                                 wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[rowid, 4]).Value2 = "'" + pa_name;
15                                 index = k - 3;
16                                 rowid = k;
17                                 k--;
18 
19                             }
20                             //退出循環時合併最後一個項目的標段
21                             wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[k - 14]).Merge(Missing.Value);
22                             wksheet.get_Range(wksheet.Cells[rowid, 4], wksheet.Cells[rowid, 4]).Value2 = "'" + pa_name;
23                             tick = DateTime.Now.ToString("yyyyMMddhhmmss");
24                             save_path = temp_path + "//" + tick + "保證金收退情況表.xls";
25                             Session["BailBackID"= tick + "保證金收退情況表.xls";
26                             Session["_BailBack"= "true";
27                             workbook.SaveAs(save_path, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);
28                             excelOperate.Dispose(worksheet, workbook, app);//關閉Excel進程
29                             //DownLoad(save_path);
30                             //Page_Close();

當然,上面的操作中會進行好幾次循環,在性能方面不太可取,園子裏的兄弟也許會有更好的方法,小弟不吝賜教了
下面我們看下幾個效果圖:





(注意:這裏提示的導出數據是指從數據庫成功取出數據,還沒有操作EXCEL對象,剛開始已經說過了,當然這個提示文字換成其它的也可以)




整個過程採用AJAX提示的,一來不刷新,二來導出時間比較長的話,可以給客戶一個良好的體驗效果,否可,用戶一點導出按鈕,半天沒反應也沒提示,客戶就覺得怎麼這麼慢的,是不是你們程序有問題,指責一大堆,有了這麼些交互提示信息,讓客戶多等幾分鐘也能承受。

     3.生成的表格包含多個sheet的操作,比如下面一種情況


繪製這張表的要求是根據選擇某年的幾月到幾月,生成這個幾個月的一個綜合情況的sheet,然後分別生成這幾個月的單獨的sheet表,生成上面表的模板,包含兩個sheet ,一個綜合月份的sheet和一個單獨月份的sheet,因爲單獨月份的sheet表現形式都是一樣的,我們可以根據選擇的月份個數Copy幾個sheet就可以了

 1  Workbooks workbooks = app.Workbooks;
 2 
 3                             _Workbook workbook = workbooks.Add(template_path + "//招標單位年度招標情況逐月統計表.xls");
 4                             Sheets sheets = workbook.Worksheets;
 5                             _Worksheet Yearsheet = (_Worksheet)sheets.get_Item(1);
 6                             _Worksheet worksheet = (_Worksheet)sheets.get_Item(2);
 7                             if (worksheet == null)
 8                             {
 9                                 return;
10                             }
11                             for (int i = 1; i < monthCount; i++)
12                                 worksheet.Copy(Missing.Value, workbook.Worksheets[2]);//月統計工作薄


Yearsheet的操作就不說了,和前面幾個一樣操作,關鍵是月份的sheet的生成,其實就是循環操作get_Item(i),代碼如下

 1  //////////////////////////////////////每月詳細統計////////////////////////////////////
 2 
 3                             int item_id = 2;
 4                             rowNum = 0; book_Amount = 0; index = 0;
 5                             bid_Amount = ""; bidser_Amount = ""; agent_Amount = 0;//清空變量
 6                             _Worksheet ws = null;
 7                             for (int i = 0; i < tableMM.Rows.Count; i++)
 8                             {
 9                                 rowNum++;
10                                 Month = tableMM.Rows[index]["DATE_MONTH"].ToString();
11                                 if (tableMM.Rows[i]["DATE_MONTH"].ToString() == Month)
12                                 {
13                                     ws = (_Worksheet)sheets.get_Item(item_id);
14                                     ws.Cells[3 + rowNum - 11= rowNum;
15                                     ws.Cells[3 + rowNum - 12= tableMM.Rows[i]["PROJNO"];
16                                     ws.Cells[3 + rowNum - 13= tableMM.Rows[i]["PROJNAME"];
17                                     ws.Cells[3 + rowNum - 14= tableMM.Rows[i]["BID_TYPE"];
18                                     ws.Cells[3 + rowNum - 15= tableMM.Rows[i]["BID_MODE"];
19                                     ws.Cells[3 + rowNum - 16= tableMM.Rows[i]["OPENDT"];
20                                     ws.Cells[3 + rowNum - 17= tableMM.Rows[i]["OPENADDRESS"];
21                                     ws.Cells[3 + rowNum - 18= tableMM.Rows[i]["BID_UNIT"];
22                                     ws.Cells[3 + rowNum - 19= tableMM.Rows[i]["NOTICE_NO"].ToString().Replace("神華國貿""");
23                                     ws.Cells[3 + rowNum - 110= tableMM.Rows[i]["BOOKAMOUNT"];
24                                     ws.Cells[3 + rowNum - 111= tableMM.Rows[i]["BIDPRICE"+ "(萬" + tableMM.Rows[i]["CURRENCY"+ ")";
25                                     ws.Cells[3 + rowNum - 112= tableMM.Rows[i]["BIDSER_AMOUNT"+ "(萬" + tableMM.Rows[i]["CURRENCY"]+")";
26                                     ws.Cells[3 + rowNum - 113= tableMM.Rows[i]["AGT_AMOUNT"];
27                                     ws.Cells[3 + rowNum - 114= "";
28                                     ws.get_Range(ws.Cells[3 + rowNum - 11], ws.Cells[3 + rowNum - 114]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
29                                     continue;
30                                 }
31 
32                                 ws.Cells[11= year + "" + bidName + GetMonth(Month) + "月份招標項目情況一覽表";
33 
34                                 //每月合計
35                                 sql = " SELECT CURRENCY, NVL(SUM(BIDPRICE),0) AS BIDPRICE,NVL(SUM(BOOKAMOUNT),0) AS BOOKAMOUNT,NVL(SUM(BIDSER_AMOUNT),0) AS BIDSER_AMOUNT,NVL(SUM(AGT_AMOUNT),0) AS AGT_AMOUNT FROM IBS_V_BID_MONTHLY_STAT" + SqlFilter +
36                                            " AND DATE_YEAR ='" + year + "' AND COMPANY_ID=" + biderID + " AND DATE_MONTH ='" + Month + "'" +
37                                            " GROUP BY CURRENCY";
38                                 System.Data.DataTable dt1 = OracleHelper.RetDataTable(sql);
39                                 for (int m = 0; m < dt1.Rows.Count; m++)
40                                 {
41                                     bid_Amount += dt1.Rows[m]["BIDPRICE"+ "(萬"+dt1.Rows[m]["CURRENCY"+ ")/r/t";
42                                     book_Amount += float.Parse(dt1.Rows[m]["BOOKAMOUNT"].ToString());
43                                     bidser_Amount += dt1.Rows[m]["BIDSER_AMOUNT"+ "(萬" + dt1.Rows[m]["CURRENCY"+ ")/r/t";
44                                     agent_Amount += float.Parse(dt1.Rows[m]["AGT_AMOUNT"].ToString());
45                                 }
46 
47                                 ws.Cells[3 + rowNum - 13= "合  計";
48                                 ws.Cells[3 + rowNum - 110= book_Amount;
49                                 ws.Cells[3 + rowNum - 111= bid_Amount;
50                                 ws.Cells[3 + rowNum - 112= bidser_Amount;
51                                 ws.Cells[3 + rowNum - 113= agent_Amount;
52                                 ws.get_Range(ws.Cells[3 + rowNum - 11], ws.Cells[3 + rowNum - 114]).Borders.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Black);
53                                 ws.Name = GetMM(Month);
54 
55                                 item_id++;
56                                 index = i; //彙總下一個月份的招標項目
57                                 i--;
58                                 rowNum = 0; book_Amount = 0;
59                                 bid_Amount = ""; bidser_Amount = ""; agent_Amount = 0;//清空變量
60                             }
61 
62                             //跳出循環時進行最後一個月份的項目彙總

用的是oracle數據庫,所以上面那個sql語句。。。 呵呵

============================================================================================
上面大致說得就差不多了,因爲是不斷循環的什麼的,可能對於大的數據量讀寫來說,比較好性能,如果大家有什麼更好的方法,可以指點下,爲了彌補等待時間過長,所以才結合了AJAX來處理。

最後我把做的一個小demo的鏈接帖出來給大家,還有一些空模板和對應生成的數據表給大家對照看下,尤其相對複雜一些的表畫應該是能畫出來的,主要看大家採用什麼樣的方法,能少循環一次就儘量少循環,呵呵~~~
   EXCEL模板讀寫說明
   http://www.justlike.com.cn/upfiles/template_xls.rar
   http://www.justlike.com.cn/upfiles/ExcelFiles.rar
   http://www.justlike.com.cn/upfiles/ExcelReportDemo.rar
(說明:最後彈出下載文件的一個頁面一直想讓其自動關掉,但是不行,如果不關掉,再點導出,不會彈出下載框,實際的處理中我們可以在導出旁邊放個下載按鈕,就像上面的效果圖裏那樣,當然可以點導出的時候讓其在網頁中直接打開,點下載的時候再彈出下載框,但是直接打開的話,文件需要生成在虛擬目錄下,不太安全,呵呵~~,看實際情況處理了)

==========================================================================================
今天補充說明下,關於那個調用ajax回調的效果,有個地方用到了所謂的“ajax嵌套調用”,如下

 1  function ExcelReportCallback(resp)
 2  {
 3       if(resp.value == "OK")
 4       {
 5            
 6              $('tipMsg').innerHTML = "<img border=/"0/" src=/"images/s_progressbar.gif/"><font color=#FF0000 style=font-weight:bold>準備導出數據,請稍等</font>";
 7              setTimeout("RedirectUrl()",1000);//延時體驗  
 8       }
 9       else
10        if(resp.value == "NO")
11        {
12           $('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>沒有找到符合該查詢條件的數據</font>";
13           $('btnExcel').disabled = false;
14        }
15       else
16       {
17           $('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>警告:導出數據出錯</font>";
18           $('btnExcel').disabled = false;
19       }
20        
21       
22  }

 

 1 function RedirectUrl()
 2  {
 3      $('tipMsg').innerHTML = "<img border=/"0/" src=/"images/ajaxloading.gif/"><font color=#7fffd4 style=font-weight:bold>正在讀寫報表文件,請稍後</font>";
 4         var ajax = new ajax_request("ExcelReport.aspx?flag=ReportByTemp&"+Math.random(), """", ReportCallback); 
 5         function ReportCallback(resp)
 6         {
 7             if(resp.value != "Error" && resp.value !="")
 8             {
 9                $('btnExcel').disabled = false;
10                $('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>數據導出成功!</font>";
11                Open("XLS_DownLoad.aspx?path="+resp.value);//window.location.href = resp.value;//
12             }
13             else
14             {
15                $('btnExcel').disabled = false;
16                $('tipMsg').innerHTML = "<font color=#FF0000 style=font-weight:bold>文件讀寫出錯,請檢查文件模板是否存在或對文件是否有讀寫權限!</font>";
17             }
18         }
19 
20  }
21  
22  function Open(url) 
23 {
24     window.open(url,'newwindow','height=1,width=1,top=1500,left=1500,toolbar=no,menubar=no,scrollbars=yes,location=no,status=no')
25 }

ExcelReportCallback(resp)原本是一個回調函數,但是裏面調用了一個RedirectUrl()方法,這個方法又包含了一個回調函數,這樣就形成了回調的嵌套,之所以這麼做,是因爲,第一個回調是處理從數據庫取出數據成功與否,如果成功了跳轉到畫EXCEL的頁面,這樣的話會出現一個空白頁等生成好後出現下載框,後來覺得是否可以嵌套一個回調來繼續一次異步操作,這樣就不會出現長時間等待的空白頁面了,而是生成好EXCEL後返回地址,或者可以返回一個文件名到XLS_DownLoad.aspx頁面直接下載,但是XLS_DownLoad.aspx也是要出現的,我嘗試過讓下載後這個頁面自動關閉,無賴做不到,所以把Open()方法裏的數據值調得讓頁面不顯示,但是狀態欄還是有顯示的。

到這裏算是寫完了,決定奢侈下,放到首頁下:),總覺得首頁的文章只有高手才能放,而且放到首頁也是一種奢侈,希望對園子裏的某些人有一定的幫助吧~~
 上面給的地址由於服務器原因下不了了,我重新貼下博客園的下載地址(其實評論裏早就貼了)
http://files.cnblogs.com/peaceli/ExcelReportDemo.rar
http://files.cnblogs.com/peaceli/shuoming.rar 文檔說明

 

注意兩個地方:


1、獲取範圍用: Excel.Range tmpRange = tmpSheet.Range[tmpSheet.Cells[1, 1], tmpSheet.Cells[1,4]];

2、在頁面下載用:

 

 private void DownLoad(string path)
        {
            if (path != null && File.Exists(path))
            {
                System.IO.FileInfo file = new System.IO.FileInfo(path);
                //清除緩衝區流中的所有內容輸出

                Response.Clear();

                //將下載保存對話框指定默認的文件名添加到HTTP頭中
                //Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name);

                Response.AddHeader("Content-Disposition", "attachment;   filename=" + System.Web.HttpUtility.UrlEncode(file.Name, System.Text.Encoding.UTF8));//避免中文出現亂碼現象  

                //在header中指定文件的大小,使瀏覽器能顯示下載過程
                Response.AddHeader("Content-Length", file.Length.ToString());

                //設置輸出流的 HTTP MIME 類型
                Response.ContentType = "application/octet-stream";

                // 發送文件流到客戶端
                Response.WriteFile(file.FullName);
                // 停止該頁的執行

                Response.End();

            }
            else
            {
                Response.Write("文件自動下載中斷,請手動下載");
            }
        }

 

3、類文件

 

using System;
using System.Web;
using Excel=Microsoft.Office.Interop.Excel;

 /// <summary>
 /// ExcelOperate 的摘要說明。Excel操作函數
 /// </summary>
 public class ExcelOperate
 {
  private object mValue = System.Reflection.Missing.Value;

  public ExcelOperate()
  {
   //
   // TODO: 在此處添加構造函數邏輯
   //
  }

  /// <summary>
  /// 合併單元格
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  public void Merge(Excel._Worksheet CurSheet,object objStartCell,object objEndCell)
  {
            CurSheet.get_Range(objStartCell, objEndCell).Merge(mValue);  
  }
  /// <summary>
  /// 設置連續區域的字體大小
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="strStartCell">開始單元格</param>
  /// <param name="strEndCell">結束單元格</param>
  /// <param name="intFontSize">字體大小</param>
  public void SetFontSize(Excel._Worksheet CurSheet, object objStartCell, object objEndCell, int intFontSize) 
  {
   CurSheet.get_Range(objStartCell, objEndCell).Font.Size = intFontSize.ToString();
  }

  /// <summary>
  /// 橫向打印
  /// </summary>
  /// <param name="CurSheet"></param>
  public void xlLandscape(Excel._Worksheet CurSheet)
  {
   CurSheet.PageSetup.Orientation=Excel.XlPageOrientation.xlLandscape;

  }
  /// <summary>
  /// 縱向打印
  /// </summary>
  /// <param name="CurSheet"></param>
  public void xlPortrait(Excel._Worksheet CurSheet)
  {
   CurSheet.PageSetup.Orientation=Excel.XlPageOrientation.xlPortrait;
  }


  /// <summary>
  /// 在指定單元格插入指定的值
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="Cell">單元格 如Cells[1,1]</param>
  /// <param name="objValue">文本、數字等值</param>
  public void WriteCell(Excel._Worksheet CurSheet,object objCell, object objValue) 
  {
   CurSheet.get_Range(objCell, mValue).Value2 = objValue;

  }

  /// <summary>
  /// 在指定Range中插入指定的值
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="StartCell">開始單元格</param>
  /// <param name="EndCell">結束單元格</param>
  /// <param name="objValue">文本、數字等值</param>
  public void WriteRange(Excel._Worksheet CurSheet,object objStartCell, object objEndCell, object objValue)
  {
   CurSheet.get_Range(objStartCell, objEndCell).Value2 = objValue;
  }
  
  /// <summary>
  /// 合併單元格,並在合併後的單元格中插入指定的值
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  /// <param name="objValue">文本、數字等值</param>
  public void WriteAfterMerge(Excel._Worksheet CurSheet,object objStartCell, object objEndCell, object objValue)
  {
   CurSheet.get_Range(objStartCell, objEndCell).Merge(mValue);
   CurSheet.get_Range(objStartCell, mValue).Value2 = objValue;

  }

  /// <summary>
  /// 爲單元格設置公式
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objCell">單元格</param>
  /// <param name="strFormula">公式</param>
  public void SetFormula(Excel._Worksheet CurSheet,object objCell, string strFormula)
  {
    CurSheet.get_Range(objCell, mValue).Formula = strFormula;
  }


  /// <summary>
  /// 單元格自動換行
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  public void AutoWrapText(Excel._Worksheet CurSheet,object objStartCell, object objEndCell)
  {
   CurSheet.get_Range(objStartCell,objEndCell).WrapText=true;
  }

  /// <summary>
  /// 設置整個連續區域的字體顏色
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  /// <param name="clrColor">顏色</param>
  public void SetColor(Excel._Worksheet CurSheet,object objStartCell, object objEndCell, System.Drawing.Color clrColor)
  {
    CurSheet.get_Range(objStartCell, objEndCell).Font.Color = System.Drawing.ColorTranslator.ToOle(clrColor);
  }

        /// <summary>
        /// 設置整個連續區域的單元格背景色
        /// </summary>
        /// <param name="CurSheet"></param>
        /// <param name="objStartCell"></param>
        /// <param name="objEndCell"></param>
        /// <param name="clrColor"></param>
        public void SetBgColor(Excel._Worksheet CurSheet, object objStartCell, object objEndCell, System.Drawing.Color clrColor)
        {
            CurSheet.get_Range(objStartCell, objEndCell).Interior.Color = System.Drawing.ColorTranslator.ToOle(clrColor);
        }

  /// <summary>
  /// 設置連續區域的字體名稱
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  /// <param name="fontname">字體名稱 隸書、仿宋_GB2312等</param>
  public void SetFontName(Excel._Worksheet CurSheet,object objStartCell, object objEndCell,string fontname)
  {
   CurSheet.get_Range(objStartCell, objEndCell).Font.Name=fontname;
  }

  /// <summary>
  /// 設置連續區域的字體爲黑體
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  public void SetBold(Excel._Worksheet CurSheet,object objStartCell, object objEndCell)
  {
   CurSheet.get_Range(objStartCell, objEndCell).Font.Bold = true;
  }
       
  
  /// <summary>
  /// 設置連續區域的邊框:上下左右都爲黑色連續邊框
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  public void SetBorderAll(Excel._Worksheet CurSheet,object objStartCell, object objEndCell)
  {
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeTop].Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeTop].LineStyle = Excel.XlLineStyle.xlContinuous;
  
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeBottom].Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeBottom].LineStyle = Excel.XlLineStyle.xlContinuous;
  
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeLeft].Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeLeft].LineStyle = Excel.XlLineStyle.xlContinuous;
  
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeRight].Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlEdgeRight].LineStyle = Excel.XlLineStyle.xlContinuous;
  
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlInsideHorizontal].Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlInsideHorizontal].LineStyle = Excel.XlLineStyle.xlContinuous;
  
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlInsideVertical].Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
   CurSheet.get_Range(objStartCell, objEndCell).Borders[Excel.XlBordersIndex.xlInsideVertical].LineStyle = Excel.XlLineStyle.xlContinuous;

  }

  /// <summary>
  /// 設置連續區域水平居中
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  public void SetHAlignCenter(Excel._Worksheet CurSheet,object objStartCell, object objEndCell)
  {
    CurSheet.get_Range(objStartCell, objEndCell).HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter;
  }
   
  /// <summary>
  /// 設置連續區域水平居左
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  public void SetHAlignLeft(Excel._Worksheet CurSheet,object objStartCell, object objEndCell)
  {
   CurSheet.get_Range(objStartCell, objEndCell).HorizontalAlignment = Excel.XlHAlign.xlHAlignLeft;
  }

  /// <summary>
  /// 設置連續區域水平居右
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  public void SetHAlignRight(Excel._Worksheet CurSheet,object objStartCell, object objEndCell)
  {
   CurSheet.get_Range(objStartCell, objEndCell).HorizontalAlignment=Excel.XlHAlign.xlHAlignRight;
  }


  /// <summary>
  /// 設置連續區域的顯示格式
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  /// <param name="strNF">如"#,##0.00"的顯示格式</param>
  public void SetNumberFormat(Excel._Worksheet CurSheet,object objStartCell, object objEndCell, string strNF)
  {
    CurSheet.get_Range(objStartCell, objEndCell).NumberFormat = strNF;
  }
  
  /// <summary>
  /// 設置列寬
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="strColID">列標識,如A代表第一列</param>
  /// <param name="dblWidth">寬度</param>
  public void SetColumnWidth(Excel._Worksheet CurSheet,string strColID, double dblWidth)
  {
   ((Excel.Range)CurSheet.Columns.GetType().InvokeMember("Item",System.Reflection.BindingFlags.GetProperty, null, CurSheet.Columns,new object[]{(strColID + ":" + strColID).ToString()})).ColumnWidth = dblWidth;
  }

  /// <summary>
  /// 設置列寬
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  /// <param name="dblWidth">寬度</param>
  public void SetColumnWidth(Excel._Worksheet CurSheet,object objStartCell, object objEndCell, double dblWidth)
  {
   CurSheet.get_Range(objStartCell,objEndCell).ColumnWidth=dblWidth;
  }


  /// <summary>
  /// 設置行高
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objStartCell">開始單元格</param>
  /// <param name="objEndCell">結束單元格</param>
  /// <param name="dblHeight">行高</param>
  public void SetRowHeight(Excel._Worksheet CurSheet,object objStartCell, object objEndCell, double dblHeight)
  {
   CurSheet.get_Range(objStartCell,objEndCell).RowHeight=dblHeight;
  }

  
  /// <summary>
  /// 爲單元格添加超級鏈接
  /// </summary>
  /// <param name="CurSheet">Worksheet</param>
  /// <param name="objCell">單元格</param>
  /// <param name="strAddress">鏈接地址</param>
  /// <param name="strTip">屏幕提示</param>
  /// <param name="strText">鏈接文本</param>
  public void AddHyperLink(Excel._Worksheet CurSheet,object objCell, string strAddress, string strTip, string strText)
  {
   CurSheet.Hyperlinks.Add(CurSheet.get_Range(objCell, objCell),strAddress, mValue, strTip, strText);
  }

  /// <summary>
  /// 另存爲xls文件
  /// </summary>
  /// <param name="CurBook">Workbook</param>
  /// <param name="strFilePath">文件路徑</param>
  public void Save(Excel._Workbook CurBook,string strFilePath)
  {
    CurBook.SaveCopyAs(strFilePath);
  }

  /// <summary>
  /// 保存文件
  /// </summary>
  /// <param name="CurBook">Workbook</param>
  /// <param name="strFilePath">文件路徑</param>
  public void SaveAs(Excel._Workbook CurBook,string strFilePath)
  {
            CurBook.SaveAs(strFilePath, mValue, mValue, mValue, mValue, mValue, Excel.XlSaveAsAccessMode.xlShared, mValue, mValue, mValue, mValue, mValue);
  }

  /// <summary>
  /// 另存爲html文件
  /// </summary>
  /// <param name="CurBook">Workbook</param>
  /// <param name="strFilePath">文件路徑</param>
  public void SaveHtml(Excel._Workbook CurBook,string strFilePath)
  {
            CurBook.SaveAs(strFilePath, Excel.XlFileFormat.xlHtml, mValue, mValue, mValue, mValue, Excel.XlSaveAsAccessMode.xlNoChange, mValue, mValue, mValue, mValue, mValue);
  }


  /// <summary>
  /// 釋放內存
  /// </summary>
  public void Dispose(Excel._Worksheet CurSheet,Excel._Workbook CurBook,Excel._Application CurExcel)
  {
   try
   {
    System.Runtime.InteropServices.Marshal.ReleaseComObject(CurSheet);
    CurSheet = null;
    CurBook.Close(false, mValue, mValue);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(CurBook);
    CurBook = null;

    CurExcel.Quit();
    System.Runtime.InteropServices.Marshal.ReleaseComObject(CurExcel);
    CurExcel = null;
           
    GC.Collect();
    GC.WaitForPendingFinalizers();
   }
   catch(System.Exception ex)
   {
        HttpContext.Current.Response.Write( "在釋放Excel內存空間時發生了一個錯誤:"+ex);
   }
   finally
   {
     foreach(System.Diagnostics.Process pro in System.Diagnostics.Process.GetProcessesByName("Excel"))                    
                 //if (pro.StartTime < DateTime.Now)
                 pro.Kill();
            }
            System.GC.SuppressFinalize(this);

   } 


 }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章