NPOI 操作 Excel 从入门到放弃(以 .xlsx 为例)

  • A+
所属分类:.NET技术
摘要

PM> Install-Package NPOI -Version 2.5.5样式总数有不能超过65535,建议使用字典方式来缓存所有样式列表;同一个单元格的样式只能由一个;如果修改了一个单元格对应的引用样式,则所有引用该样式的单元格都会生效

NPOI 操作 Excel 从入门到放弃(以 .xlsx 为例)

PM> Install-Package NPOI -Version 2.5.5

基础操作

实例化 Workbook

public static IWorkbook CreateWorkbook(string fileName, Stream ms = null) {     var fileType = Path.GetExtension(fileName);      var isSuffixMatched = new Func<string, string, bool>((extension, suffix) =>         string.Equals(extension, suffix, StringComparison.OrdinalIgnoreCase));      if (isSuffixMatched(fileType, ".xls"))     {         return ms == null ? new HSSFWorkbook() : new HSSFWorkbook(ms);     }      if (isSuffixMatched(fileType, ".xlsx"))     {         return ms == null ? new XSSFWorkbook() : new XSSFWorkbook(ms);     }      throw new NotSupportedException("不支持的文件类型"); } 

基于模板实例化 Workbook

using FileStream fs = File.OpenRead(fileName); Workbook = WorkbookFactory.Create(stream); 

基于 IFormFile 实例化 Workbook

using var ms = new MemoryStream(); file.CopyTo(ms); ms.Seek(0, SeekOrigin.Begin); Workbook = CreateWorkbook(_fileName, ms);  # 保存为 FileContentResult using var ms = new MemoryStream(); ms.Seek(0, SeekOrigin.Begin); Workbook.Write(ms); var bytes = ms.ToArray(); var file = new FileContentResult(bytes, "application/vnd.ms-excel") { FileDownloadName = _fileName }; 

Vue 下载 FileContentResult 类型的 Excel 文件

 download(){      var fileBytes = this.File.FileContents      var bytes = window.atob(fileBytes)      var n = bytes.length      var u8arr = new Uint8Array(n)      while (n--) {      u8arr[n] = bytes.charCodeAt(n)      }      this.download(u8arr,this.FileName)  },  download(file, fileName) {      const blob = new Blob()      if (window.navigator.msSaveBlob) {          // IE10+ 使用的下载方式          return window.navigator.msSaveBlob(blob, fileName)      }      // 其他浏览器下载方式      const reader = new FileReader()      reader.readAsDataURL(blob)      reader.onload = e => {          const a = document.createElement('a')          a.download = fileName          a.href = e.target.result          document.body.appendChild(a)          a.click()          document.body.removeChild(a)      }  } 

进阶操作

宽度设置

//自适应宽度 cell.Sheet.AutoSizeColumn(cell.ColumnIndex); //以 100 个字符长度为宽度 cell.Sheet.SetColumnWidth(cell.ColumnIndex, 100 * 256); 

样式设置

样式总数有不能超过65535,建议使用字典方式来缓存所有样式列表;同一个单元格的样式只能由一个;如果修改了一个单元格对应的引用样式,则所有引用该样式的单元格都会生效

private static ICellStyle CreateBaseStyle(IWorkbook wb, Action<ICellStyle> styleAction,Action<IFont> fontAction) {     var style = wb.CreateCellStyle();      #region 边框设置     style.BorderTop = BorderStyle.Thin;     style.BorderBottom = BorderStyle.Thin;     style.BorderLeft = BorderStyle.Thin;     style.BorderRight = BorderStyle.Thin;     #endregion      #region 背景色设置     style.FillForegroundColor = HSSFColor.White.Index;     style.FillPattern = FillPattern.SolidForeground;     #endregion      #region 布局设置     style.Alignment = HorizontalAlignment.Left;     style.VerticalAlignment = VerticalAlignment.Center;     #endregion      styleAction?.Invoke(style);      #region 字体设置     var font = wb.CreateFont();     font.Color = HSSFColor.Black.Index;     font.FontName = "微软雅黑";     font.IsBold = false;     font.FontHeightInPoints = 10;     font.Underline = FontUnderlineType.None;     fontAction?.Invoke(font);     style.SetFont(font);     #endregion      return style; }  

自定义背景色

var style = CreateBaseStyle(wb, sa => {     try     {         sa.FillForegroundColor = 0;         ((XSSFColor)sa.FillForegroundColorColor).SetRgb(new byte[] { 255, 199, 206 });     }     catch (Exception e)     {         Trace.WriteLine(e);         sa.FillForegroundColor = HSSFColor.Rose.Index;     } }, fa => { }); 

设置超链接

public static void SetHyperlink(ICell cell, string description, string address) {     var hy = cell.Sheet.Workbook.GetCreationHelper().CreateHyperlink(HyperlinkType.Document);     hy.Address = address;     cell.Hyperlink = hy;     cell.SetCellValue(description); } 

设置下拉框

支持对非法数据校验,但是不支持空数据校验

public static void SetDropDownList<TEnum>(ICell cell) where TEnum : struct, Enum {     var attributes = typeof(TEnum).GetMembers()         .SelectMany(member =>             member.GetCustomAttributes(typeof(DescriptionAttribute), true).Cast<DescriptionAttribute>())         .Select(x => x.Description).ToArray();      if (Enum.TryParse(cell.StringCellValue, out TEnum result))     {         cell.SetCellValue(result.GetDescription());     }      var dvHelper = cell.Sheet.GetDataValidationHelper();     var constraint = dvHelper.CreateExplicitListConstraint(attributes);     var addressList = new CellRangeAddressList(         cell.RowIndex,         65535,         cell.ColumnIndex,         cell.ColumnIndex);     var dataValidation = dvHelper.CreateValidation(constraint, addressList);     dataValidation.CreateErrorBox("错误", "请选择符合要求的数值");     dataValidation.ShowErrorBox = true;     cell.Sheet.AddValidationData(dataValidation); } 

合并单元格

设置合并区域样式时,需要设置每个单元格;取合并区域对应的数据时,只需要取左上角第一个单元格的值即可

public static void AddMergedRegions(ISheet sheet,     params (string cellValue, CellRangeAddress cellRangeAddress, ICellStyle style)[] regions) {     for (var i = 0; i < regions.Length; i++)     {         var (cellValue, cellRangeAddress, style) = regions[i];         for (var rowIndex = cellRangeAddress.FirstRow;             rowIndex <= cellRangeAddress.LastRow;             rowIndex++)         {             var row = sheet.GetRow(rowIndex) ?? sheet.CreateRow(rowIndex);             for (var colIndex = cellRangeAddress.FirstColumn;                 colIndex <= cellRangeAddress.LastColumn;                 colIndex++)             {                 var cell = row.GetCell(colIndex) ?? row.CreateCell(colIndex, CellType.String);                 if (cell.ColumnIndex == cellRangeAddress.FirstColumn &&                     cell.RowIndex == cellRangeAddress.FirstRow)                 {                     cell.SetCellValue(cellValue);                 }                  cell.CellStyle = style;             }         }          sheet.AddMergedRegion(cellRangeAddress);     } } 

创建批注

批注数量不能超过65535,建议总量不要超过1000

public static void SetCellComment(ISheet sheet, int rowNum, int colNum, string message, ICellStyle cellStyle) {     var row = sheet.GetRow(rowNum) ?? sheet.CreateRow(rowNum);     var cell = row.GetCell(colNum) ?? row.CreateCell(colNum);     cell.RemoveCellComment();      var creationHelper = sheet.Workbook.GetCreationHelper();     //使用批注默认位置     //var anchor = creationHelper.CreateClientAnchor();     //指定批注位置     var anchor = drawing.CreateAnchor(       0,       0,       0,       0,       colNum + 1,       rowNum + 1,       colNum + 4,       rowNum + 4);     var drawing = sheet.DrawingPatriarch ?? sheet.CreateDrawingPatriarch();     var comment = drawing.CreateCellComment(anchor);     comment.String = creationHelper.CreateRichTextString(message);     comment.Author = "hippieZhou";     comment.Visible = false;     comment.Address = cell.Address;     cell.CellComment = comment;      cell.CellStyle = cellStyle; }  // 移除批注  var sheet = workbook.GetSheetAt(i);  var comments = sheet.GetCellComments();  if (comments.Any())  {      //移除批注      foreach (var cell in comments.Select(comment =>          sheet.GetRow(comment.Key.Row).GetCell(comment.Key.Column)))      {          cell?.RemoveCellComment();      }  }