前言
NPOI 是 POI 項目的.NET版本,它不使用 Office COM 組件,不需要安裝 Microsoft Office,目前支持 Office 2003 和 2007 版本。
1、整個Excel表格叫做工作表:WorkBook(工作薄),包含的叫頁(工作表):Sheet;行:Row;單元格Cell。
2、NPOI是POI的C#版本,NPOI的行和列的index都是從0開始
3、POI讀取Excel有兩種格式一個是HSSF,另一個是XSSF。 HSSF和XSSF的區別如下:
HSSF is the POI Project's pure Java implementation of the Excel '97(-2007) file format.
XSSF is the POI Project's pure Java implementation of the Excel 2007 OOXML (.xlsx) file format.
即:HSSF適用2007以前的版本,XSSF適用2007版本及其以上的。
本文將通過詳細例子聊聊如何利用NPOI導出Excel,導入Excel,以及合並單元格,設置樣式,輸入公式的內容。
在使用NPOI之前需要先下載NPOI插件包
下載地址:https://archive.codeplex.com/?p=npoi
NPOI導出Excel
在MVC中 可以使用 FileResult 導出Excel輸出到前端下載
/// <summary>
/// 導出數據
/// </summary>
/// <returns></returns>
public FileResult ExportJK(string Keyword)
{
TablePager tp = new TablePager();
tp.Keyword = Keyword;
DateTime now = DateTime.Now;
Random rad = new Random();
int num = rad.Next(10, 100);
string pathString = Request.ApplicationPath;
var mappath = Server.MapPath(pathString);
var domainPath = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
if (!Directory.Exists(string.Format("{0}\\Report", domainPath)))
{
Directory.CreateDirectory(string.Format("{0}\\Report", domainPath));
}
//string fileName = string.Format(@"{0}\temp\SummarySalary_{1}.xlsx", mappath, StringHelper.GetRandom(8));
string fileName = String.Format(@"{0}\\Report\\Invoice{2}_{1}.xlsx", domainPath, num.ToString(), now.ToString("yyyyMMddHHmmss"));
string download = string.Format(@"Invoice{1}_{0}.xlsx", num.ToString(), now.ToString("yyyyMMddHHmmss"));
IDeliverService _Service = CoreServiceFactory.Used.Build<IDeliverService>();
List<ExportTemplate1> exps = _Service.ExportPrintTable(this.AppUser, tp);
System.IO.FileStream fs;
try
{
//創建Excel文件的對象
IWorkbook workbook = new HSSFWorkbook();
//添加一個sheet
NPOI.SS.UserModel.ISheet sheet1 = workbook.CreateSheet("Sheet1");
//給sheet1添加第一行的頭部標題
NPOI.SS.UserModel.IRow row1 = sheet1.CreateRow(0);
row1.CreateCell(0).SetCellValue("導出單號");
row1.CreateCell(1).SetCellValue("渠道Code");
row1.CreateCell(2).SetCellValue("渠道名稱");
row1.CreateCell(3).SetCellValue("銷售小組");
row1.CreateCell(4).SetCellValue("銷售人員");
row1.CreateCell(5).SetCellValue("倉庫");
row1.CreateCell(6).SetCellValue("快遞公司");
row1.CreateCell(7).SetCellValue("銷售單號");
row1.CreateCell(8).SetCellValue("客戶名稱");
row1.CreateCell(9).SetCellValue("制單時間");
row1.CreateCell(10).SetCellValue("發貨單金額");
row1.CreateCell(11).SetCellValue("代收款");
row1.CreateCell(12).SetCellValue("運單號");
//將數據逐步寫入sheet1各個行
for (int i = 0; i < exps.Count; i++)
{
NPOI.SS.UserModel.IRow rowtemp = sheet1.CreateRow(i + 1);
rowtemp.CreateCell(0).SetCellValue(exps[i].InvoiceNo);
rowtemp.CreateCell(1).SetCellValue(exps[i].SalesChannelCode);
rowtemp.CreateCell(2).SetCellValue(exps[i].SalesChannelName);
rowtemp.CreateCell(3).SetCellValue(exps[i].SalesGroupName);
rowtemp.CreateCell(4).SetCellValue(exps[i].SalesUserName);
rowtemp.CreateCell(5).SetCellValue(exps[i].WarehouseName);
rowtemp.CreateCell(6).SetCellValue(exps[i].ShipperName);
rowtemp.CreateCell(7).SetCellValue(exps[i].OrderNo);
rowtemp.CreateCell(8).SetCellValue(exps[i].CustomerName);
rowtemp.CreateCell(9).SetCellValue(exps[i].CreateDate);
rowtemp.CreateCell(10).SetCellValue(exps[i].Receivable.ToString("n"));
rowtemp.CreateCell(11).SetCellValue(exps[i].AgencyFund.ToString("n"));
rowtemp.CreateCell(12).SetCellValue(exps[i].LogisticCode);
}
// 寫入到客戶端
fs = new FileStream(fileName, FileMode.OpenOrCreate);
workbook.Write(fs);
fs.Seek(0, SeekOrigin.Begin);
//fs = NExcelHelper.DataTableToExcel<ExportTemplate1>(exps, fileName, "未發貨列表", true, "");
}
catch (Exception)
{
throw new Exception();
}
return File(fs, "application/vnd.ms-excel", download);
}
效果如下圖


NPOI導入Excel
導入Excel一般是指通過Execl將數據批量保存到數據庫中,所以相對導出要麻煩很多,也要嚴謹很多。
1. 在前台頁面提供Excel模板下載

2. 前端選擇文件

注意form表單需要加上 enctype="multipart/form-data",目的是使請求允許提交文件類型
3. 后台程序
/// <summary>
/// 導入商品數據
/// </summary>
/// <param name="FilePath"></param>
/// <param name="user"></param>
/// <returns></returns>
public MsgResult ImportGoods(string FilePath, ApplicationUser user)
{
MsgResult result = new MsgResult();
result.ErrorNote = new List<ErrorNote>();
DateTime now = DateTime.Now;
var dt = NExcelHelper.ImportExcelFile(FilePath, "Sheet1", false);
if (dt.Rows.Count <= 0)
{
result.ResultCode = ResultCode.Error;
result.Message = "檢測到文件中沒有任何行";
return result;
}
if (dt.Columns.Count != 12)
{
result.ResultCode = ResultCode.Error;
result.Message = "檢測到文件的數據格式不正確";
return result;
}
//事務處理
using (DbContextTransaction dbTransaction = productRepository.EFDb.Database.BeginTransaction())
{
try
{
for (int i = 0; i < dt.Rows.Count; i++)
{
decimal UnitPrice = 0.00M; //售價
decimal Cost = 0.00M; //進價
decimal MinPrice = 0.00M; //最小
decimal MaxPrice = 0.00M;
int IsAllow = 1;
string brandname = dt.Rows[i][0].ToString();
string proName = dt.Rows[i][2].ToString();
string proUnitPrice = dt.Rows[i][7].ToString();
string proCost = dt.Rows[i][6].ToString();
string proMinPrice = dt.Rows[i][8].ToString();
string proMaxPrice = dt.Rows[i][9].ToString();
string proIsdecimal = dt.Rows[i][11].ToString();
bool IsSucc1 = decimal.TryParse(proUnitPrice, out UnitPrice);
bool IsSucc2 = decimal.TryParse(proCost, out Cost);
bool IsSucc3 = decimal.TryParse(proMinPrice, out MinPrice);
bool IsSucc4 = decimal.TryParse(proMaxPrice, out MaxPrice);
int.TryParse(proIsdecimal, out IsAllow);
if (string.IsNullOrWhiteSpace(brandname))
{
throw new Exception("檢測到品牌為空!");
}
List<Brand> brands = brandRepository.QueryBrands(t => t.BrandName == brandname && t.State != State.Deleted).ToList();
if (brands != null)
{
if (brands.Count > 1)
{
throw new Exception("系統檢測到品牌名稱 " + brandname + " 存在二義性!");
}
}
if (string.IsNullOrWhiteSpace(proName))
{
throw new Exception("檢測到有商品名稱為空!");
}
if (string.IsNullOrWhiteSpace(proUnitPrice))
{
throw new Exception("檢測到有銷售金額為空!");
}
if (!IsSucc1)
{
throw new Exception("系統檢測到有 售價 不是一個金額類型!");
}
if (!string.IsNullOrWhiteSpace(proCost) && !IsSucc2)
{
throw new Exception("系統檢測到有 進價 不是一個金額類型!");
}
if (!string.IsNullOrWhiteSpace(proMinPrice) && !IsSucc3)
{
throw new Exception("系統檢測到有 最低售價 不是一個金額類型!");
}
if (!string.IsNullOrWhiteSpace(proMinPrice) && !IsSucc4)
{
throw new Exception("系統檢測到有 最高售價 不是一個金額類型!");
}
Product pd = new Product();
pd.BrandId = brands.FirstOrDefault().BrandId;
pd.BarCode = dt.Rows[i][1].ToString();
pd.ProductName = proName;
pd.ShortName = dt.Rows[i][3].ToString();
pd.Spec = dt.Rows[i][4].ToString();
pd.Unit = dt.Rows[i][5].ToString();
pd.Cost = Cost;
pd.UnitPrice = UnitPrice;
pd.MinPrice = MinPrice;
pd.MaxPrice = MaxPrice;
pd.Remark = dt.Rows[i][10].ToString();
pd.AllowTheDecimal = IsAllow == 1;
pd.Status = ProductStatus.Normal;
int res = productRepository.InsertForProduct(pd);
if (res <= 0)
{
throw new Exception("第 " + i + " 行插入失敗,請檢查");
}
}
dbTransaction.Commit();
}
catch (Exception ex)
{
dbTransaction.Rollback();
result.ResultCode = ResultCode.Exception;
result.Message = ex.Message;
return result;
}
}
result.ResultCode = ResultCode.Success;
result.Message = "商品批量導入成功!";
return result;
}
代碼中我使用了事物整體處理,並且加了一些必要的驗證,保證數據的安全性,真實性。
設置單元格樣式
設置單元格樣式時需要注意,務必創建一個新的樣式對象進行設置,否則會將工作表所有單元格的樣式一同設置,它們應該共享的是一個樣式對象:
ICellStyle style = workbook.CreateCellStyle(); //設置單元格的樣式:水平對齊居中 style.Alignment = HorizontalAlignment.CENTER; //新建一個字體樣式對象
IFont font = workbook.CreateFont();
//設置字體加粗樣式
font.Boldweight = short.MaxValue;
//使用SetFont方法將字體樣式添加到單元格樣式中
style.SetFont(font); //將新的樣式賦給單元格 cell.CellStyle = style;
設置單元格的高度:實際是設置其所在行高,所以要在單元格所在行上設置行高,行高設置數值好像是像素點的1/20,所以*20以便達到設置效果;
設置單元格的寬度:實際上是設置其所在列寬,所以要在單元格所在列上設置(列的設置在工作表上),寬度數值好像是字符的1/256,所以*256以便達到設置效果。
//設置單元格的高度 row.Height = 30 * 20; //設置單元格的寬度
sheet.SetColumnWidth(0, 30 * 256);
合並單元格:合並單元格實際上是聲明一個區域,該區域中的單元格將進行合並,合並后的內容與樣式以該區域最左上角的單元格為准。
//設置一個合並單元格區域,使用上下左右定義CellRangeAddress區域 //CellRangeAddress四個參數為:起始行,結束行,起始列,結束列 sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, 10));
添加公式:使用Cell的CellFormula來設置公式,是一個字符串,公式前不需要加=號。
//通過Cell的CellFormula向單元格中寫入公式 //注:直接寫公式內容即可,不需要在最前加'=' ICell cell2 = sheet.CreateRow(1).CreateCell(0); cell2.CellFormula = "HYPERLINK(\"測試圖片.jpg\",\"測試圖片.jpg\")";
將工作簿寫入文件查看效果:
//將工作簿寫入文件
using (FileStream fs = new FileStream("生成效果.xls", FileMode.Create, FileAccess.Write))
{
workbook.Write(fs);
}
效果如下圖:


