1.前言
相信大家在工作中經常要遇到一些導入導出Execl操作。學習貴在分享,分享使人快樂,園子里的前輩已經有很多好的文章,鄙人也是能力有限,在這里把這些好的文章總結,方便以后再工作中使用。
NPOI:是 POI 項目的 .NET 版本。POI是一個開源的Java讀寫Excel、WORD等微軟OLE2組件文檔的項目。
NPOI是構建在POI 3.x版本之上的,它可以在沒有安裝Office的情況下對Word/Excel文檔進行讀寫操作。
它不使用Office COM組件(Microsoft.Office.Interop.XXX.dll),不需要安裝Microsoft Office,支持對Office 97-2003的文件格式,功能比較強大。
能夠讀寫幾乎所有的Office 97-2003文件格式,至少能夠支持Word, PowerPoint, Excel, Visio的格式。
1、整個Excel表格叫做工作表:WorkBook(工作薄),包含的叫頁(工作表):Sheet;行:Row;單元格Cell。
2、NPOI是POI的C#版本,NPOI的行和列的index都是從0開始
3、POI讀取Excel有兩種格式一個是HSSF,另一個是XSSF。
HSSF和XSSF的區別如下:
HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,擴展名是.xls
XSSFWorkbook:是操作Excel2007的版本,擴展名是.xlsx
即:HSSF適用2007以前的版本,XSSF適用2007版本及其以上的。
HSSFWorkbook對應的就是Excel文件 工作簿,
HSSFSheet對應的就是Excel中sheet 工作表,
HSSFCell對應的就是Excel的單元格,
HSSFRow對應的就是Excel的行
.NET調用NPOI組件導入導出Excel的操作類
此NPOI操作類的優點如下:
(1)支持web及winform從DataTable導出到Excel;
(2)生成速度很快;
(3)准確判斷數據類型,不會出現身份證轉數值等問題;
(4)如果單頁條數大於65535時會新建工作表;
(5)列寬自適應;
2.簡單用法
namespace 導入導出 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnInput_Click(object sender, EventArgs e) { #region 導入到DataTable using (FileStream stream = File.OpenRead("huangjinfeng.xls")) { IWorkbook workbook = new HSSFWorkbook(stream); ISheet sheet = workbook.GetSheet("員工捐款信息表"); DataTable table = new DataTable(); IRow headerRow = sheet.GetRow(0); int cellCount = headerRow.LastCellNum; int rowCount = sheet.LastRowNum; for (int i = headerRow.FirstCellNum; i < cellCount; i++) { DataColumn column = new DataColumn(headerRow.GetCell(i).StringCellValue); table.Columns.Add(column); } for (int i = (sheet.FirstRowNum + 1); i <= rowCount; i++) { IRow row = sheet.GetRow(i); DataRow dataRow = table.NewRow(); if (row != null) { for (int j = row.FirstCellNum; j < cellCount; j++) { if (row.GetCell(j) != null) dataRow[j] = row.GetCell(j); } } table.Rows.Add(dataRow); } this.dataGridView1.DataSource = table; MessageBox.Show("導入數據成功"); } #endregion #region 導入到數據庫 //using (FileStream stream = File.OpenRead("huangjinfeng.xls")) //{ // string sql="INSERT INTO [dbo].[DonationDetail]([dUserName],[dcID],[dAmount],[dDate],[disdelete],[dCreateTime])"; // RenderToDb(stream,sql); // MessageBox.Show("SQL"); //} #endregion } private void btnOut_Click(object sender, EventArgs e) { DonationEntities2 db = new DonationEntities2(); var lists = db.CreateObjectSet<DonationDetail>().Where(c => c.disdelete == 0).ToList(); #region 自由導出 HSSFWorkbook workbook = new HSSFWorkbook(); //2.創建工作表 ISheet sheet = workbook.CreateSheet("員工捐款信息表"); IRow rowHeader = sheet.CreateRow(0); rowHeader.CreateCell(0, CellType.String).SetCellValue("DId"); rowHeader.CreateCell(1, CellType.String).SetCellValue("員工姓名"); rowHeader.CreateCell(2, CellType.String).SetCellValue("DcId"); rowHeader.CreateCell(3, CellType.String).SetCellValue("捐款金額"); rowHeader.CreateCell(4, CellType.String).SetCellValue("捐款日期"); rowHeader.CreateCell(5, CellType.String).SetCellValue("是否刪除"); rowHeader.CreateCell(6, CellType.String).SetCellValue("創建日期"); for (int i = 0; i < lists.Count; i++) { IRow row = sheet.CreateRow(i + 1); //為指定的行添加列 row.CreateCell(0, CellType.String).SetCellValue(lists[i].dId); row.CreateCell(1, CellType.String).SetCellValue(lists[i].dUserName); row.CreateCell(2, CellType.String).SetCellValue(lists[i].dcID); row.CreateCell(3, CellType.String).SetCellValue(lists[i].dAmount.ToString()); row.CreateCell(4, CellType.String).SetCellValue(Convert.ToDateTime(lists[i].dDate.ToString())); row.CreateCell(5, CellType.String).SetCellValue(lists[i].disdelete); row.CreateCell(6, CellType.String).SetCellValue(Convert.ToDateTime(lists[i].dCreateTime.ToString())); } //使用文件流做數據的寫入 using (FileStream fss = new FileStream("huangjinfeng.xls", FileMode.Create)) { workbook.Write(fss); } MessageBox.Show("導出數據成功"); #endregion }


} }
3.項目中的Execl導入導出
工作中我一般主要是用到的MVC,在這里就說說我們項目中一般的處理過程。先看看我自己寫的一個NPOIBase父類。
public class NPOIBase : ActionResult { public IWorkbook _workbook { get; set; } public ISheet _sheet { get; set; } public ICellStyle _titleStyle { get; set; } public ICellStyle _leftStyle { get; set; } public ICellStyle _centerStyle { get; set; } public ICellStyle _rightStyle { get; set; } public ICellStyle _headStyle { get; set; } public ICellStyle _leftborderStyle { get; set; } public ICellStyle _rightborderStyle { get; set; } public ICellStyle _noneRightBorderStyle { get; set; } public ICellStyle _noneLeftBorderStyle { get; set; } public ICellStyle _noneLeftAndRightBorderStyle { get; set; } public ICellStyle _borderStyle { get; set; }
public override void ExecuteResult(ControllerContext context) { }
public void IniNPOI(bool isHeadBorder = false, string sheetName = "") { _workbook = new HSSFWorkbook(); _sheet = string.IsNullOrWhiteSpace(sheetName) ? _workbook.CreateSheet() : _workbook.CreateSheet(sheetName); IniStyle(isHeadBorder); }

public void FillHeadCell(IRow row, int colIndex, string value, ICellStyle cellStyle = null,
NPOI.SS.Util.CellRangeAddress mergedCellRangeAddress = null) { if (_sheet == null || row == null) return; if (cellStyle == null) cellStyle = _headStyle; FillCell(row, colIndex, value, cellStyle, mergedCellRangeAddress); _sheet.SetColumnWidth(colIndex, (Encoding.Default.GetBytes(value.Trim()).Length + 4) * 256); }
public void FillCell(IRow row, int colIndex, string value, ICellStyle cellStyle = null,
NPOI.SS.Util.CellRangeAddress mergedCellRangeAddress = null) { if (_sheet == null || row == null) return; ICell titleSum = row.CreateCell(colIndex); titleSum.SetCellValue(value); if (cellStyle != null) titleSum.CellStyle = cellStyle; else if (_centerStyle != null) titleSum.CellStyle = _centerStyle; if (mergedCellRangeAddress != null) _sheet.AddMergedRegion(mergedCellRangeAddress); }
public void FillCell(IRow row, int colIndex, double value, ICellStyle cellStyle = null,
NPOI.SS.Util.CellRangeAddress mergedCellRangeAddress = null) { if (_sheet == null || row == null) return; ICell titleSum = row.CreateCell(colIndex); titleSum.SetCellValue(value); if (cellStyle != null) titleSum.CellStyle = cellStyle; else if (_centerStyle != null) titleSum.CellStyle = _centerStyle; if (mergedCellRangeAddress != null) _sheet.AddMergedRegion(mergedCellRangeAddress); }
public void ResponseOutPutExcelStream(string fildName) { if (string.IsNullOrWhiteSpace(fildName)) fildName = DateTime.Now.ToString("yyyyMMddHHmmss.xls"); if (fildName.ToLower().IndexOf(".xls") == -1) fildName += ".xls"; HttpContext.Current.Response.ContentType = "application/vnd.ms-excel"; HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", fildName)); HttpContext.Current.Response.Clear(); MemoryStream file = new MemoryStream(); _workbook.Write(file); file.WriteTo(HttpContext.Current.Response.OutputStream); HttpContext.Current.Response.End(); }
public void SetPrint(bool isLandscape = false, bool isFitToPage = false, double topMargin = 0, double rightMargin = 0,
double bottomMargin = 0.5, double leftMargin = 0, short scale = 100) { _sheet.PrintSetup.Landscape = isLandscape; _sheet.SetMargin(MarginType.TopMargin, topMargin); _sheet.SetMargin(MarginType.RightMargin, rightMargin); _sheet.SetMargin(MarginType.LeftMargin, leftMargin); _sheet.SetMargin(MarginType.BottomMargin, bottomMargin); _sheet.PrintSetup.PaperSize = 9; _sheet.PrintSetup.Scale = scale; _sheet.FitToPage = isFitToPage; if (isFitToPage) { _sheet.PrintSetup.FitWidth = 1; _sheet.PrintSetup.FitHeight = 0; } } }
這個由於父類是繼承的ActionResult,我們用起來就比較方便,在Action中,直接使用就可以了。代碼示例如下...
public class BaseMaterialsExcelResult : NPOIBase { string[] __headers = null; IList<BaseMaterials> __BaseMaterialsList; public BaseMaterialsExcelResult(IList<BaseMaterials> BaseMaterialsList) { __BaseMaterialsList = BaseMaterialsList; __headers = new string[] { "序號", "材料", "型號", "推薦供應商", "出庫數量", "入庫數量", "結存" }; }
public override void ExecuteResult(ControllerContext context) { if (__BaseMaterialsList == null || __BaseMaterialsList.Count() == 0) return; IniNPOI(); int rowIndex = 0; foreach (var item in __BaseMaterialsList) { #region 新建表,填充列頭,樣式 int colIndex = 0; if (rowIndex == 65535 || rowIndex == 0) { if (rowIndex != 0) _sheet = _workbook.CreateSheet(); IRow headerRow = _sheet.CreateRow(rowIndex); foreach (var head in __headers) FillHeadCell(headerRow, colIndex++, head); rowIndex = 1; } #endregion #region 填充內容 IRow dataRow = _sheet.CreateRow(rowIndex); colIndex = 0; FillCell(dataRow, colIndex++, rowIndex); FillCell(dataRow, colIndex++, item.Name); FillCell(dataRow, colIndex++, item.Type); FillCell(dataRow, colIndex++, item.ProviderName); FillCell(dataRow, colIndex++, item.OutStorageCount.ToString()); FillCell(dataRow, colIndex++, item.StorageCount.ToString()); FillCell(dataRow, colIndex++, item.StockCount.ToString()); #endregion rowIndex++; } _sheet.CreateFreezePane(1, 1, 1, 1); ResponseOutPutExcelStream("BaseMaterials.xls"); } }
控制器中的代碼如下:
public class HomeController : Controller { BluedonStockEntities db = new BluedonStockEntities(); public List<BaseMaterials> GetList() { return db.CreateObjectSet<BaseMaterials>().Where(c => true).ToList(); } public ActionResult Index() { var list = GetList(); return View(list); } public ActionResult Execl() { var list = GetList(); return new BaseMaterialsExcelResult(list); } }
4.問題總結。
- 在實例化了一個WorkBook之后,最好添加一個sheet,雖然在最新版的Npoi.net中自動添加了,但是遇到遷移到原來版本就會出現問題。所以我建議還是最少添加一個sheet
- 在從單元格取值時要注意單元格的類型,一定要用對應的類型的方法來取單元格中的對應類型的值,如果不確定,那只能是強制轉換成為string類型,畢竟string類型是excel中其他類型都可以轉換過來的
- 在獲取sheet中的某一行或者某一行的某一個單元格的時候,還要注意就是一定要確保創建了該行,並且取單元格還要確保創建了單元格,否則會報Null reference not to object 這個我們經常會看到的異常信息。在外層一定要加上try捕獲異常
- 合並單元格是sheet的工作,因此需要獲取對應的sheet,然后調用其AddMergedRegion方法合並單元格,在合並單元格的時候,我們不需要確保該單元格已經存在或創建。
- 在為單元格設置樣式的過程中,我們會發現所有和樣式相關的類的創建都是通過workBook.Create(Font)..這種方式來執行的,我們不可以直接new一個類的實例。
- 如果前面的工作都已經做好,需要把內存中的excel表寫到硬盤上時,需要調用workBook.write()方法,傳入一個文件流進行創建。在這里有可能會出現一個問題,就是你要創建的文件你已經打開了,這時程序就會出現異常,因此我們在調試程序的時候一定要記得打開了excel文件以后要關閉
- 最后需要注意的就是文件流,在我們把excel寫到硬盤上以后,要顯式的調用其close()方法關閉文件流。因為如果不關閉文件流的話,以后就會出現無法重新創建該文件的錯誤,並且會提示 某文件正由另一進程使用,因此該進程無法訪問此文件。
簡單用法的源碼【點擊下載】
MVC版的源碼【點擊下載】
感謝你的支持,為我點個贊吧!