ASP.NET產生Excel的幾種方式
還記得剛跨進程式設計這個領域的時候,當時有處理到一個問題,使用者在Web上面產生Excel檔造成記憶體崩潰的問題。查看了程式發現那時候的開發方式是使用【 Excel 物件模型來存取】,但這種方式主要是運用在單機使用上,若在Web上面使用常常會有很多問題產生,例如 : 記憶體無法釋放等問題。
那時候剛進行,完全不知道該如何解決,老闆只好請我跟微軟詢問該如何解決,得到的答案確是Microsoft 不建議在Server端使用直接存取 Excel 物件模型的方式來控制 Excel 檔案,後來因為某些因素就離開了該公司,也沒有解決此問題,這個問題變成我寫程式以來最挫折的事情。但很幸運的是,後來所接觸的程式開發,除了報表外(可以透過ReportViewer幫我們產生),要匯出Excel的檔案都不是很複雜的,因此都可以使用Html表格畫表在透過改變ContentType的方式來產生,日子久了也漸漸淡忘這個悲慘的往事 ~"~。
這陣子再翻以前廠商開發的程式的時候發現了另一種使用方式NPOI Library,這是由幾個佛心來的開發者共同開發出來給大家使用的,詳細資訊可以參考MSDN的學習園地。下面大概說明一下三種常見的轉出Excel方式。
1. 透過改變ContentType為Excel標頭檔的方式,這個方法是最快速也最簡單,完全不需要使用使用額外的Library就可以轉出Excel檔,但缺點就是彈性很小,因為是使用HTML偽裝成Excel檔,若有比較特殊的需求就比較難達成,且轉出的檔案在新版的Office上面開啟會跳出警告訊息如下圖。
2. 使用Excel物件模型,這個就是我心中的痛~"~,但是由於在Web使用上不太適合,且有其它的替代方案,因此我就沒有深入研究了,關於Lock的更多的解決方是可以自行參考如何 回收 被 VB 及 C# 呼叫的 Excel 應用程式,簡單的使用方式如下。
3. 使用NPOI Library,下面的使用方式所產生檔案是跟上面一模一樣的,這只是一個簡單的範例,NPOI功能強大且並沒有使用到 Excel 的任何東西,它直接深入 OLE Compound Document (複合文件)格式內去存取資料,也可以直接控制到各種儲存格的資訊(顏色,儲存格格式與樣式等),並將它物件導向化,外部開發人員只需要利用 NPOI 提供的屬性就可以控制 Office 格式的檔案資料,且更不會有Lock的問題。
這樣比較下來NPOI幾乎跟上面兩種方使比起來幾乎是零缺點,但是我還是不常使用到它~"~,因為說真的,專案開發時程的限制,讓自己沒辦法追求到完美,有時候一些很簡單的清單,我就會用偷懶的方式Response.Write來處理了。
本文範例 :
ExprotExcel.zip
那時候剛進行,完全不知道該如何解決,老闆只好請我跟微軟詢問該如何解決,得到的答案確是Microsoft 不建議在Server端使用直接存取 Excel 物件模型的方式來控制 Excel 檔案,後來因為某些因素就離開了該公司,也沒有解決此問題,這個問題變成我寫程式以來最挫折的事情。但很幸運的是,後來所接觸的程式開發,除了報表外(可以透過ReportViewer幫我們產生),要匯出Excel的檔案都不是很複雜的,因此都可以使用Html表格畫表在透過改變ContentType的方式來產生,日子久了也漸漸淡忘這個悲慘的往事 ~"~。
這陣子再翻以前廠商開發的程式的時候發現了另一種使用方式NPOI Library,這是由幾個佛心來的開發者共同開發出來給大家使用的,詳細資訊可以參考MSDN的學習園地。下面大概說明一下三種常見的轉出Excel方式。
1. 透過改變ContentType為Excel標頭檔的方式,這個方法是最快速也最簡單,完全不需要使用使用額外的Library就可以轉出Excel檔,但缺點就是彈性很小,因為是使用HTML偽裝成Excel檔,若有比較特殊的需求就比較難達成,且轉出的檔案在新版的Office上面開啟會跳出警告訊息如下圖。
Response.Clear(); Response.AddHeader("content-disposition", "attachment;filename=Excel.xls"); Response.ContentType = "application/vnd.ms-excel"; Response.ContentEncoding = System.Text.Encoding.GetEncoding("utf-8"); Response.Write("<style type='text/css'>table{ vnd.ms-excel.numberformat:@;font-family:%u5FAE%u8EDF%u6B63%u9ED1%u9AD4;font-size:10pt; }</style>"); Response.Write(""); Response.Write(""); foreach (DataRow row in dt.Rows) { Response.Write(""); Response.Write(""); Response.Write(""); Response.Write(""); Response.Write(""); } Response.Write("<table border="1"><tr><td>Name</td><td>Birthday</td><td>Age</td></tr>"); Response.Write("<tr>"); Response.Write("<td>" + row["Name"].ToString() + "</td>"); Response.Write("<td>" + row["Birthday"].ToString() + "</td>"); Response.Write("<td>" + row["Age"].ToString() + "</td>"); Response.Write("</tr>"); Response.Write("</table>"); Response.End();
2. 使用Excel物件模型,這個就是我心中的痛~"~,但是由於在Web使用上不太適合,且有其它的替代方案,因此我就沒有深入研究了,關於Lock的更多的解決方是可以自行參考如何 回收 被 VB 及 C# 呼叫的 Excel 應用程式,簡單的使用方式如下。
protected void Button2_Click(object sender, EventArgs e) { string execelfullpath = Server.MapPath("~/Excel.xls"); Microsoft.Office.Interop.Excel.Application xlapp = new Microsoft.Office.Interop.Excel.Application(); Microsoft.Office.Interop.Excel.Workbook wbook = xlapp.Workbooks.Add(1); Microsoft.Office.Interop.Excel.Worksheet wsheet = (Microsoft.Office.Interop.Excel.Worksheet)wbook.Worksheets[1]; wsheet.Name = "Sheet Name"; Microsoft.Office.Interop.Excel.Range range; //標題列 wsheet.Cells[1, 1] = "Name"; wsheet.Cells[1, 2] = "Birthday"; wsheet.Cells[1, 3] = "Age"; FormateExcelStyle((Microsoft.Office.Interop.Excel.Range)wsheet.Cells[1, 1]); FormateExcelStyle((Microsoft.Office.Interop.Excel.Range)wsheet.Cells[1, 2]); FormateExcelStyle((Microsoft.Office.Interop.Excel.Range)wsheet.Cells[1, 3]); int introw = 2; foreach (DataRow dr in dt.Rows) { wsheet.Cells[introw, 1] = dr["Name"].ToString(); wsheet.Cells[introw, 2] = dr["Birthday"].ToString(); wsheet.Cells[introw, 3] = dr["Age"].ToString(); FormateExcelStyle((Microsoft.Office.Interop.Excel.Range)wsheet.Cells[introw, 1]); FormateExcelStyle((Microsoft.Office.Interop.Excel.Range)wsheet.Cells[introw, 2]); FormateExcelStyle((Microsoft.Office.Interop.Excel.Range)wsheet.Cells[introw, 3]); introw++; } if (File.Exists(execelfullpath)) File.Delete(execelfullpath); wbook.SaveAs(execelfullpath, Microsoft.Office.Interop.Excel.XlFileFormat.xlXMLSpreadsheet, Missing.Value, Missing.Value, false, false, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value ); KillExcel(xlapp); Response.Redirect("Excel.xls"); } //儲存格格式 private void FormateExcelStyle(Microsoft.Office.Interop.Excel.Range range) { range.Font.Name = "微軟正黑體"; range.BorderAround(XlLineStyle.xlContinuous, XlBorderWeight.xlThin, XlColorIndex.xlColorIndexAutomatic, null); } //刪除Excel執行個體,解決Excel發生Lock的問題 [DllImport("user32.dll", SetLastError = true)] static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); static void KillExcel(Microsoft.Office.Interop.Excel.Application m_objExcel) { if (m_objExcel != null) { int lpdwProcessId; GetWindowThreadProcessId(new IntPtr(m_objExcel.Hwnd), out lpdwProcessId); System.Diagnostics.Process.GetProcessById(lpdwProcessId).Kill(); System.Threading.Thread.Sleep(2000); } }
3. 使用NPOI Library,下面的使用方式所產生檔案是跟上面一模一樣的,這只是一個簡單的範例,NPOI功能強大且並沒有使用到 Excel 的任何東西,它直接深入 OLE Compound Document (複合文件)格式內去存取資料,也可以直接控制到各種儲存格的資訊(顏色,儲存格格式與樣式等),並將它物件導向化,外部開發人員只需要利用 NPOI 提供的屬性就可以控制 Office 格式的檔案資料,且更不會有Lock的問題。
HSSFWorkbook workbook = new HSSFWorkbook(); using (MemoryStream ms = new MemoryStream()) { // 新增試算表。 ISheet sheet = workbook.CreateSheet("Sheet Name"); // 儲存格字型 NPOI.SS.UserModel.IFont font = workbook.CreateFont(); font.FontName = "微軟正黑體"; // 建立儲存格樣式。 ICellStyle style = workbook.CreateCellStyle(); style.SetFont(font); style.BorderBottom = NPOI.SS.UserModel.BorderStyle.THIN; style.BottomBorderColor = NPOI.HSSF.Util.HSSFColor.BLACK.index; style.BorderLeft = NPOI.SS.UserModel.BorderStyle.THIN; style.LeftBorderColor = NPOI.HSSF.Util.HSSFColor.BLACK.index; style.BorderRight = NPOI.SS.UserModel.BorderStyle.THIN; style.RightBorderColor = NPOI.HSSF.Util.HSSFColor.BLACK.index; style.BorderTop = NPOI.SS.UserModel.BorderStyle.THIN; style.TopBorderColor = NPOI.HSSF.Util.HSSFColor.BLACK.index; //插入標題列 IRow row = sheet.CreateRow(0); row.CreateCell(0).SetCellValue("Name"); row.CreateCell(1).SetCellValue("Birthday"); row.CreateCell(2).SetCellValue("Age"); row.Cells[0].CellStyle = style; row.Cells[1].CellStyle = style; row.Cells[2].CellStyle = style; int i = 1; foreach (DataRow dr in dt.Rows) { row = sheet.CreateRow(i); row.CreateCell(0).SetCellValue(dr["Name"].ToString()); row.CreateCell(1).SetCellValue(dr["Birthday"].ToString()); row.CreateCell(2).SetCellValue(dr["Age"].ToString()); row.Cells[0].CellStyle = style; row.Cells[1].CellStyle = style; row.Cells[2].CellStyle = style; i++; } workbook.Write(ms); Response.AddHeader("Content-Disposition", string.Format("attachment; filename=Excel.xls")); Response.BinaryWrite(ms.ToArray()); } workbook = null;
這樣比較下來NPOI幾乎跟上面兩種方使比起來幾乎是零缺點,但是我還是不常使用到它~"~,因為說真的,專案開發時程的限制,讓自己沒辦法追求到完美,有時候一些很簡單的清單,我就會用偷懶的方式Response.Write來處理了。
本文範例 :
ExprotExcel.zip
留言
張貼留言
您好,我是 Lawrence,這裡是我的開發筆記的網誌,如果你對我的文章有任何疑問或者有錯誤的話,歡迎留言讓我知道。