1、Excel導入及導出問題產生:
從接觸.net到如今一直在維護一個DataTable導出到Excel的類,時不時還會維護一個導入類。如下是時不時就會出現的問題:
導出問題:
若是是asp.net,你得在服務器端裝Office,幾百M呢,還得及時更新它,以防漏洞,還得設定權限容許ASP.net訪問COM+,據說若是導出過程當中出問題可能致使服務器宕機。
Excel會把只包含數字的列進行類型轉換,原本是文本型的,它非要把你轉成數值型的,像身份證後三位變成000,編號000123會變成123,夠智能吧,夠鬱悶吧。不過這些都仍是能夠變通解決的,在他們前邊加上一個字母,讓他們不僅包含數字。
導出時,若是你的字段內容以"-"或"="開頭,Excel好像把它當成了公式什麼的,接下來就出錯,提示:相似,保存到Sheet1的問題
導入問題:
Excel會根據你的 Excel文件前8行分析數據類型,若是正好你前8行某一列只是數字,那它會認爲你這一列就是數值型的,而後,身份證,手機,編號都轉吧變成相似這樣的1.42702E+17格式,日期列變成 包含日期和數字的,亂的很,能夠經過改註冊表讓Excel分析整個表,但若是整列都是數字,那這個問題仍是解決不了。
以上問題,通常人初次作時確定得上網查查吧,一個問題接着另外一個問題,查到你鬱郁而死,還有不少問題沒解決,最終感受已經解決的不錯了,但還不能保證某一天還會出個什麼問題。
2、使用第三方開源組件導入及導出Excel的解決方案:
偶然間發現了NPOI與MyXls,相見恨晚,害的我在Excel上浪費了那麼多時間,他們倆的好處是:就是.net的自定義類庫,能夠直接對Excel進行讀或寫,而不依賴Office 的 Excel,這無論對於ASP.net或Winform都很是有利,不用擔憂Excel進程的釋放問題,服務器安全,設置,導出,導入「Excel智能識別」,公式日期等問題,能夠說之前的Excel問題,全都不用管了,它們能夠很好的幫你解決,NPOI || MyXls == 研究幾年Excel。
NPOI開源地址:
http://npoi.codeplex.com/
MyXls開源地址:
http://sourceforge.net/projects/myxls/
3、下面來兩個簡單入門例子:
MyXls 快速入門例子:
/// <summary> /// MyXls簡單Demo,快速入門代碼 /// </summary> /// <param name="dtSource"></param> /// <param name="strFileName"></param> /// <remarks>MyXls認爲Excel的第一個單元格是:(1,1)</remarks> public static void ExportEasy(DataTable dtSource, string strFileName) { XlsDocument xls = new XlsDocument(); Worksheet sheet = xls.Workbook.Worksheets.Add("Sheet1"); //填充表頭 foreach (DataColumn col in dtSource.Columns) { sheet.Cells.Add(1, col.Ordinal + 1, col.ColumnName); } //填充內容 for (int i = 0; i < dtSource.Rows.Count; i++) { for (int j = 0; j < dtSource.Columns.Count; j++) { sheet.Cells.Add(i + 2, j + 1, dtSource.Rows[i][j].ToString()); } } //保存 xls.FileName = strFileName; xls.Save(); }
NPOI 快速入門例子:
/// <summary> /// NPOI簡單Demo,快速入門代碼 /// </summary> /// <param name="dtSource"></param> /// <param name="strFileName"></param> /// <remarks>NPOI認爲Excel的第一個單元格是:(0,0)</remarks> public static void ExportEasy(DataTable dtSource, string strFileName) { HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.CreateSheet(); //填充表頭 HSSFRow dataRow = sheet.CreateRow(0); foreach (DataColumn column in dtSource.Columns) { dataRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName); } //填充內容 for (int i = 0; i < dtSource.Rows.Count; i++) { dataRow = sheet.CreateRow(i + 1); for (int j = 0; j < dtSource.Columns.Count; j++) { dataRow.CreateCell(j).SetCellValue(dtSource.Rows[i][j].ToString()); } } //保存 using (MemoryStream ms = new MemoryStream()) { using (FileStream fs = new FileStream(strFileName, FileMode.Create, FileAccess.Write)) { workbook.Write(fs); } } workbook.Dispose(); }
4、接下來是封裝的能夠用在實際項目中的類,實現的功能有(僅NPOI):
- 支持web及winform從DataTable導出到Excel。
- 生成速度很快。
- 準確判斷數據類型,不會出現身份證轉數值等上面提到的一系列問題。
- 若是單頁條數大於65535時會新建工做表。
- 列寬自適應。
- 支持讀取Excel。
- 調用方便,只一調用一個靜態類就OK了。
- 由於測試期間發現MyXls導出速度要比NPOI慢3倍,而NPOI既能知足咱們的導出需求,又能很好的知足咱們的導入需求,因此只針對NPOI進行全方位功能實現及優化。
NPOI操做類 using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.IO; using System.Text; using NPOI; using NPOI.HPSF; using NPOI.HSSF; using NPOI.HSSF.UserModel; using NPOI.HSSF.Util; using NPOI.POIFS; using NPOI.Util; namespace SNF.Utilities { public class NPOIHelper { /// <summary> /// DataTable導出到Excel文件 /// </summary> /// <param name="dtSource">源DataTable</param> /// <param name="strHeaderText">表頭文本</param> /// <param name="strFileName">保存位置</param> public static void Export(DataTable dtSource, string strHeaderText, string strFileName) { using (MemoryStream ms = Export(dtSource, strHeaderText)) { using (FileStream fs = new FileStream(strFileName, FileMode.Create, FileAccess.Write)) { byte[] data = ms.ToArray(); fs.Write(data, 0, data.Length); fs.Flush(); } } } /// <summary> /// DataTable導出到Excel的MemoryStream /// </summary> /// <param name="dtSource">源DataTable</param> /// <param name="strHeaderText">表頭文本</param> public static MemoryStream Export(DataTable dtSource, string strHeaderText) { HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.CreateSheet(); #region 右擊文件 屬性信息 { DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); dsi.Company = "NPOI"; workbook.DocumentSummaryInformation = dsi; SummaryInformation si = PropertySetFactory.CreateSummaryInformation(); si.Author = "文件做者信息"; //填加xls文件做者信息 si.ApplicationName = "建立程序信息"; //填加xls文件建立程序信息 si.LastAuthor = "最後保存者信息"; //填加xls文件最後保存者信息 si.Comments = "做者信息"; //填加xls文件做者信息 si.Title = "標題信息"; //填加xls文件標題信息 si.Subject = "主題信息";//填加文件主題信息 si.CreateDateTime = DateTime.Now; workbook.SummaryInformation = si; } #endregion HSSFCellStyle dateStyle = workbook.CreateCellStyle(); HSSFDataFormat format = workbook.CreateDataFormat(); dateStyle.DataFormat = format.GetFormat("yyyy-mm-dd"); //取得列寬 int[] arrColWidth = new int[dtSource.Columns.Count]; foreach (DataColumn item in dtSource.Columns) { arrColWidth[item.Ordinal] = Encoding.GetEncoding(936).GetBytes(item.ColumnName.ToString()).Length; } for (int i = 0; i < dtSource.Rows.Count; i++) { for (int j = 0; j < dtSource.Columns.Count; j++) { int intTemp = Encoding.GetEncoding(936).GetBytes(dtSource.Rows[i][j].ToString()).Length; if (intTemp > arrColWidth[j]) { arrColWidth[j] = intTemp; } } } int rowIndex = 0; foreach (DataRow row in dtSource.Rows) { #region 新建表,填充表頭,填充列頭,樣式 if (rowIndex == 65535 || rowIndex == 0) { if (rowIndex != 0) { sheet = workbook.CreateSheet(); } #region 表頭及樣式 { HSSFRow headerRow = sheet.CreateRow(0); headerRow.HeightInPoints = 25; headerRow.CreateCell(0).SetCellValue(strHeaderText); HSSFCellStyle headStyle = workbook.CreateCellStyle(); headStyle.Alignment = CellHorizontalAlignment.CENTER; HSSFFont font = workbook.CreateFont(); font.FontHeightInPoints = 20; font.Boldweight = 700; headStyle.SetFont(font); headerRow.GetCell(0).CellStyle = headStyle; sheet.AddMergedRegion(new Region(0, 0, 0, dtSource.Columns.Count - 1)); headerRow.Dispose(); } #endregion #region 列頭及樣式 { HSSFRow headerRow = sheet.CreateRow(1); HSSFCellStyle headStyle = workbook.CreateCellStyle(); headStyle.Alignment = CellHorizontalAlignment.CENTER; HSSFFont font = workbook.CreateFont(); font.FontHeightInPoints = 10; font.Boldweight = 700; headStyle.SetFont(font); foreach (DataColumn column in dtSource.Columns) { headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName); headerRow.GetCell(column.Ordinal).CellStyle = headStyle; //設置列寬 sheet.SetColumnWidth(column.Ordinal, (arrColWidth[column.Ordinal] + 1) * 256); } headerRow.Dispose(); } #endregion rowIndex = 2; } #endregion #region 填充內容 HSSFRow dataRow = sheet.CreateRow(rowIndex); foreach (DataColumn column in dtSource.Columns) { HSSFCell newCell = dataRow.CreateCell(column.Ordinal); string drValue = row[column].ToString(); switch (column.DataType.ToString()) { case "System.String"://字符串類型 newCell.SetCellValue(drValue); break; case "System.DateTime"://日期類型 DateTime dateV; DateTime.TryParse(drValue, out dateV); newCell.SetCellValue(dateV); newCell.CellStyle = dateStyle;//格式化顯示 break; case "System.Boolean"://布爾型 bool boolV = false; bool.TryParse(drValue, out boolV); newCell.SetCellValue(boolV); break; case "System.Int16"://整型 case "System.Int32": case "System.Int64": case "System.Byte": int intV = 0; int.TryParse(drValue, out intV); newCell.SetCellValue(intV); break; case "System.Decimal"://浮點型 case "System.Double": double doubV = 0; double.TryParse(drValue, out doubV); newCell.SetCellValue(doubV); break; case "System.DBNull"://空值處理 newCell.SetCellValue(""); break; default: newCell.SetCellValue(""); break; } } #endregion rowIndex++; } using (MemoryStream ms = new MemoryStream()) { workbook.Write(ms); ms.Flush(); ms.Position = 0; sheet.Dispose(); //workbook.Dispose();//通常只用寫這一個就OK了,他會遍歷並釋放全部資源,但當前版本有問題因此只釋放sheet return ms; } } /// <summary> /// 用於Web導出 /// </summary> /// <param name="dtSource">源DataTable</param> /// <param name="strHeaderText">表頭文本</param> /// <param name="strFileName">文件名</param> public static void ExportByWeb(DataTable dtSource, string strHeaderText, string strFileName) { HttpContext curContext = HttpContext.Current; // 設置編碼和附件格式 curContext.Response.ContentType = "application/vnd.ms-excel"; curContext.Response.ContentEncoding = Encoding.UTF8; curContext.Response.Charset = ""; curContext.Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(strFileName, Encoding.UTF8)); curContext.Response.BinaryWrite(Export(dtSource, strHeaderText).GetBuffer()); curContext.Response.End(); } /// <summary>讀取excel /// 默認第一行爲標頭 /// </summary> /// <param name="strFileName">excel文檔路徑</param> /// <returns></returns> public static DataTable Import(string strFileName) { DataTable dt = new DataTable(); HSSFWorkbook hssfworkbook; using (FileStream file = new FileStream(strFileName, FileMode.Open, FileAccess.Read)) { hssfworkbook = new HSSFWorkbook(file); } HSSFSheet sheet = hssfworkbook.GetSheetAt(0); System.Collections.IEnumerator rows = sheet.GetRowEnumerator(); HSSFRow headerRow = sheet.GetRow(0); int cellCount = headerRow.LastCellNum; for (int j = 0; j < cellCount; j++) { HSSFCell cell = headerRow.GetCell(j); dt.Columns.Add(cell.ToString()); } for (int i = (sheet.FirstRowNum + 1); i <= sheet.LastRowNum; i++) { HSSFRow row = sheet.GetRow(i); DataRow dataRow = dt.NewRow(); for (int j = row.FirstCellNum; j < cellCount; j++) { if (row.GetCell(j) != null) dataRow[j] = row.GetCell(j).ToString(); } dt.Rows.Add(dataRow); } return dt; } } }
5、NPOI操做類的調用方法
DataTable dt_Grade = Tools.Data.GetDataTable(strSQL);
filename += "績效考覈結果分數統計表";
PMS.Common.NPOIHelper.ExportByWeb(dt_Grade, filename, filename+".xls");html
6、效果展現
7、NPOI類庫下載:
NPOI 下載地址: http://npoi.codeplex.com/releasesweb
官方學習網站 : http://tonyqus.sinaapp.com/spring
信息來源:http://www.cnblogs.com/yongfa365/archive/2010/05/10/NPOI-MyXls-DataTable-To-Excel-From-Excel.html安全
8、參考地址:
NPOI導出Excel表功能實現(多個工做簿):
http://www.cnblogs.com/zhengjuzhuan/archive/2010/02/01/1661103.html
在 Server 端存取 Excel 檔案的利器:NPOI Library:
http://msdn.microsoft.com/zh-tw/ee818993.aspx
ASP.NET使用NPOI類庫導出Excel:
http://www.cnblogs.com/niunan/archive/2010/03/30/1700706.html
9、總結:
經過以上分析,咱們不難發現,用NPOI或MyXls代替是Excel是很明智的,在發文前,我看到NPOI及MyXls仍然在活躍的更新中。在使用過程當中發現這兩個組件極類似,之前看過文章說他們使用的內核是同樣的。還有NPOI是國人開發的,且有相關中文文檔,在不少地方有相關引用,下載量也很大。而且它支持Excel,看到MyXls相關問題基本上沒人回答,因此推薦使用NPOI。MyXls能夠直接Cell.Font.Bold操做,而NPOI得使用CellType多少感受有點麻煩。