NPOI是指構建在POI 3.x版本之上的一個程序,NPOI可以在沒有安裝Office的情況下對Word或Excel文檔進行讀寫操作。
NPOI是一個開源的C#讀寫Excel、WORD等微軟OLE2組件文檔的項目。
(一)傳統操作
Excel遇到的問題:
1、如果是
.NET,需要在服務器端裝
Office,且及時更新它,以防漏洞,還需要設定權限允許
.NET訪問
COM+,如果在導出過程中出問題可能導致服務器宕機。
2、
Excel會把只包含數字的列進行類型轉換,本來是文本型的,
Excel會將其轉成數值型的,比如編號000123會變成123。
3、導出時,如果字段內容以“-”或“=”開頭,Excel會把它當成公式進行,會報錯。
4、
Excel會根據
Excel文件前8行分析數據類型,如果正好你前8行某一列只是數字,那它會認為該列為數值型,自動將該列轉變成類似1.42702
E+17格式,日期列變成包含日期和數字的。
(二)使用
NPOI的優勢
1、您可以完全免費使用該框架
2、包含了大部分EXCEL的特性(單元格樣式、數據格式、公式等等)
3、專業的技術支持服務(24*7全天候) (非免費)
5、采用面向接口的設計架構( 可以查看 NPOI.SS 的
命名空間)
6、同時支持文件的導入和導出
7、基於.net 2.0 也支持xlsx 和 docx格式(當然也支持.net 4.0)
8、來自全世界大量成功且真實的測試Cases
9、大量的實例代碼
11、你不需要在服務器上安裝微軟的
Office,可以避免版權問題。
12、使用起來比
Office PIA的
API更加方便,更人性化。
13、你不用去花大力氣維護
NPOI,
NPOI
Team會不斷更新、改善
NPOI,絕對省成本。
14、不僅僅對與Excel可以進行操作,對於doc、ppt文件也可以做對應的操作
NPOI之所以強大,並不是因為它支持導出
Excel,而是因為它支持導入
Excel,並能“理解”
OLE2文檔結構,這也是其他一些Excel讀寫庫比較弱的方面。通常,讀入並理解結構遠比導出來得復雜,因為導入你必須假設一切情況都是可能的,而生成你只要保證滿足你自己需求就可以了,如果把導入需求和生成需求比做兩個集合,那么生成需求通常都是導入需求的子集,這一規律不僅體現在
Excel讀寫庫中,也體現在
pdf讀寫庫中,目前市面上大部分的
pdf庫僅支持生成,不支持導入。
上代碼:
項目在使用前需要在NuGet程序包里下載NPOI;
/// <summary> /// 將List數據導入到excel中 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="list">需要導入的List集合</param> /// <param name="isColumnWritten">DataTable的列名是否要導入</param> /// <param name="sheetName">要導入的excel的sheet的名稱</param> /// <param name="fileName">文件夾路徑</param> /// <returns>導入數據行數(包含列名那一行)</returns> public static int ListToExcel<T>(List<T> list, string sheetName, bool isColumnWritten, string fileName) { DataTable data = new DataTable(); if (list == null) { throw new ArgumentNullException("data"); } if (string.IsNullOrEmpty(sheetName)) { throw new ArgumentNullException(sheetName); } if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException(fileName); } IWorkbook workbook = null; if (fileName.IndexOf(".xlsx", StringComparison.Ordinal) > 0) { workbook = new XSSFWorkbook(); } else if (fileName.IndexOf(".xls", StringComparison.Ordinal) > 0) { workbook = new HSSFWorkbook(); } FileStream fs = null; try { //excel表格列數 int columnCount = 0; fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); ISheet sheet; if (workbook != null) { sheet = workbook.CreateSheet(sheetName); } else { return -1; } int j; int count; //寫入DataTable的列名,寫入單元格中 if (isColumnWritten) { var row = sheet.CreateRow(0); T tempT = list.FirstOrDefault(); List<string> listColumn = GetClassInfo.GetColumn<T>(tempT); if (listColumn != null) { columnCount = listColumn.Count; for (int c = 0; c < columnCount; c++) { row.CreateCell(c).SetCellValue(listColumn[c]); } } count = 1; } else { count = 0; } //遍歷循環List具體數據項 object value = null; int ti = 0; int tcell = 0; foreach (var t in list) { if (t != null) { System.Reflection.PropertyInfo[] properties = t.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); if (properties.Length > 0) { var row = sheet.CreateRow(count); tcell = 0; foreach (System.Reflection.PropertyInfo item in properties) { value = item.GetValue(t, null); if (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String")) { row.CreateCell(tcell).SetCellValue(value != null ? value.ToString() : ""); } tcell++; } ti++; ++count; } } } //將文件流寫入到excel workbook.Write(fs); return count; } catch (IOException ioex) { throw new IOException(ioex.Message); } catch (Exception ex) { throw new Exception(ex.Message); } finally { if (fs != null) { fs.Close(); } } }
/// <summary> /// 將DataTable數據導入到excel中 /// </summary> /// <param name="data">要導入的數據</param> /// <param name="isColumnWritten">DataTable的列名是否要導入</param> /// <param name="sheetName">要導入的excel的sheet的名稱</param> /// <param name="fileName">文件夾路徑</param> /// <returns>導入數據行數(包含列名那一行)</returns> public static int DataTableToExcel(DataTable data, string sheetName, bool isColumnWritten, string fileName) { if (data == null) { throw new ArgumentNullException("data"); } if (string.IsNullOrEmpty(sheetName)) { throw new ArgumentNullException(sheetName); } if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException(fileName); } IWorkbook workbook = null; if (fileName.IndexOf(".xlsx", StringComparison.Ordinal) > 0) { workbook = new XSSFWorkbook(); } else if (fileName.IndexOf(".xls", StringComparison.Ordinal) > 0) { workbook = new HSSFWorkbook(); } FileStream fs = null; try { fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); ISheet sheet; if (workbook != null) { sheet = workbook.CreateSheet(sheetName); } else { return -1; } int j; int count; //寫入DataTable的列名,寫入單元格中 if (isColumnWritten) { var row = sheet.CreateRow(0); for (j = 0; j < data.Columns.Count; ++j) { row.CreateCell(j).SetCellValue(data.Columns[j].ColumnName); } count = 1; } else { count = 0; } //遍歷循環datatable具體數據項 int i; for (i = 0; i < data.Rows.Count; ++i) { var row = sheet.CreateRow(count); for (j = 0; j < data.Columns.Count; ++j) { row.CreateCell(j).SetCellValue(data.Rows[i][j].ToString()); } ++count; } //將文件流寫入到excel workbook.Write(fs); return count; } catch (IOException ioex) { throw new IOException(ioex.Message); } catch (Exception ex) { throw new Exception(ex.Message); } finally { if (fs != null) { fs.Close(); } } }
/// <summary> /// 將excel中的數據導入到DataTable中 /// </summary> /// <param name="sheetName">excel工作薄sheet的名稱</param> /// <param name="isFirstRowColumn">第一行是否是DataTable的列名</param> /// <param name="fileName">文件路徑</param> /// <returns>返回的DataTable</returns> public static DataTable ExcelToDataTable(string sheetName, bool isFirstRowColumn, string fileName) { if (string.IsNullOrEmpty(sheetName)) { throw new ArgumentNullException(sheetName); } if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException(fileName); } var data = new DataTable(); IWorkbook workbook = null; FileStream fs = null; try { fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); if (fileName.IndexOf(".xlsx", StringComparison.Ordinal) > 0) { workbook = new XSSFWorkbook(fs); } else if (fileName.IndexOf(".xls", StringComparison.Ordinal) > 0) { workbook = new HSSFWorkbook(fs); } ISheet sheet = null; if (workbook != null) { //如果沒有找到指定的sheetName對應的sheet,則嘗試獲取第一個sheet sheet = workbook.GetSheet(sheetName) ?? workbook.GetSheetAt(0); } if (sheet == null) return data; var firstRow = sheet.GetRow(0); //一行最后一個cell的編號 即總的列數 int cellCount = firstRow.LastCellNum; int startRow; if (isFirstRowColumn) { for (int i = firstRow.FirstCellNum; i < cellCount; ++i) { var cell = firstRow.GetCell(i); var cellValue = cell.StringCellValue; if (cellValue == null) continue; var column = new DataColumn(cellValue); data.Columns.Add(column); } startRow = sheet.FirstRowNum + 1; } else { startRow = sheet.FirstRowNum; } //最后一列的標號 var rowCount = sheet.LastRowNum; for (var i = startRow; i <= rowCount; ++i) { var row = sheet.GetRow(i); //沒有數據的行默認是null if (row == null) continue; var dataRow = data.NewRow(); for (int j = row.FirstCellNum; j < cellCount; ++j) { //同理,沒有數據的單元格都默認是null if (row.GetCell(j) != null) dataRow[j] = row.GetCell(j).ToString(); } data.Rows.Add(dataRow); } return data; } catch (IOException ioex) { throw new IOException(ioex.Message); } catch (Exception ex) { throw new Exception(ex.Message); } finally { if (fs != null) { fs.Close(); } } }
