NPOI导入,导出
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NPOI导入,导出相关的知识,希望对你有一定的参考价值。
概述
NPOI 是 POI 项目的 .NET 版本。POI是一个开源的Java读写Excel、WORD等微软OLE2组件文档的项目。NPOI 可以在没有安装 Office 或者相应环境的机器上对 WORD/EXCEL 文档进行读写,包含了大部分EXCEL的特性(单元格样式、数据格式、公式等等)。(复制百度百科)
表格导入导出的操作经常会碰到,最近抽空专门封装了一下。
遵循的原则:
1. 一行数据对应一个数据实体,需提供它们二者间的映射关系。
2. 支持合并单元格的导入导出(导出数据只支持竖向合并,导入数据遇到横向合并的单元格会抛出异常),图片未支持
导出的复杂在于样式的控制,当解决这一问题后后续操作就很简单了,而样式解析相关的代码行数也真的是不少,因为对API的不熟悉,连写带测折腾了不少时间。
导入因为行为的多变性,要求程序员调用时将具体的读写逻辑以策略的形式传入。所以代码量相对来说要少得多。其实也是避开了一些难题,比如公式符号,数字的上标,下标的解析(尚不知道怎么解决)。
导出效果
表格导入
单元格数据的解析
1. 先依据单元格类的数据类型cell.CellType(枚举类型),读取其内部保存的数据,如果类型是公式的,则依据cell.CachedFormulaResultType再进行一次判别。
2. 从单元格读到数据后还再进一步完成一次类型的转换,将它转换为我们需要的类型。
/// <summary> /// 扩展 /// </summary> public static class IRowExtension { /// <summary> /// 读取表格中某单元格的值,如果单元格是合并单元格,则竖向合并的正常返回,横向合并的抛出异常 /// </summary> /// <typeparam name="A">要读取的数据类型</typeparam> /// <param name="row">Excel中的某行数据</param> /// <param name="index">行内的列下标</param> /// <returns></returns> public static A GetValue<A>(this IRow row, int index) { try { //获取单元格 ICell cell = row.GetCell(index); //判别是否合并单元格 if (cell.IsMergedCell) { ISheet sheet = cell.Sheet; for (int i = 0, length = sheet.NumMergedRegions; i < length; i++) { var cellRange = sheet.GetMergedRegion(i); if (cell.ColumnIndex >= cellRange.FirstColumn && cell.ColumnIndex <= cellRange.LastColumn && cell.RowIndex >= cellRange.FirstRow && cell.RowIndex <= cellRange.LastRow) { if (cellRange.FirstColumn != cellRange.LastColumn) { throw new Exception("不能有横向合并的单元格"); } cell = sheet.GetRow(cellRange.FirstRow).GetCell(cellRange.FirstColumn); } } } return GetValue<A>(cell); } catch (Exception ex) { //读取单元格信息时出错 throw new Exception("出错位置的列下标:" + index + ",报错信息:" + ex.Message); } } /// <summary> /// 读取表格中某单元格的值,如果单元格是合并单元格,则竖向合并的正常返回,横向合并的抛出异常 /// </summary> /// <typeparam name="A">要读取的数据类型</typeparam> /// <param name="cell">Excel中的某单元格</param> /// <returns></returns> private static A GetValue<A>(ICell cell) { //依据单元格数据类型读取数据,并强转为目标类型 switch (cell.CellType) { case CellType.Boolean: return cell.BooleanCellValue.ChangeType<A>(); case CellType.Error: return ErrorEval.GetText(cell.ErrorCellValue).ChangeType<A>(); case CellType.Numeric: if (DateUtil.IsCellDateFormatted(cell)) { return cell.DateCellValue.ChangeType<A>(); } else { return cell.NumericCellValue.ChangeType<A>(); } case CellType.String: string strValue = cell.StringCellValue; if (string.IsNullOrEmpty(strValue)) { return default(A); } else { return strValue.ChangeType<A>(); } case CellType.Formula://公式 switch (cell.CachedFormulaResultType) { case CellType.Boolean: return cell.BooleanCellValue.ChangeType<A>(); case CellType.Error: return ErrorEval.GetText(cell.ErrorCellValue).ChangeType<A>(); case CellType.Numeric: if (DateUtil.IsCellDateFormatted(cell)) { return cell.DateCellValue.ChangeType<A>(); } else { return cell.NumericCellValue.ChangeType<A>(); } case CellType.String: string str = cell.StringCellValue; if (string.IsNullOrEmpty(str)) { return default(A); } else { return str.ChangeType<A>(); } case CellType.Unknown: case CellType.Blank: default: return default(A); } case CellType.Unknown: case CellType.Blank: default: return default(A); } } }
类型转换
/// <summary> /// 调用Convert.ChangeType /// </summary> /// <typeparam name="T">要返回的类型</typeparam> /// <param name="token">待转换的值</param> /// <returns>转换后的值</returns> public static T ChangeType<T>(this object token) { object result = ChangeType(token, typeof(T)); if (result == null) { return default(T); } return (T)result; } /// <summary> /// 把对象类型转换为指定类型 /// </summary> /// <param name="value">要转换的值</param> /// <param name="targetType">目标类型</param> /// <returns> 转化后的指定类型的对象</returns> public static object ChangeType(this object value, Type targetType) { if (value == null) { return null; } if (targetType.IsNullableType()) { targetType = targetType.GetUnderlyingType(); } if (targetType.IsEnum) { return Enum.Parse(targetType, value.ToString()); } if (targetType == typeof(Guid)) { return Guid.Parse(value.ToString()); } return Convert.ChangeType(value, targetType); }
导出代码的主体部分
策略接口
/// <summary> /// 提供每行数据的操作策略(不同的实现方式,达成不同的导入目标,存储目标不必一定是数据库) /// 通过GetValue读取某单元格数据 /// </summary> public interface ExcelImportHelper { /// <summary> /// 判断本行数据是否数据库中已存在(有则会进行修改,没有则会新建) /// </summary> /// <param name="row">Excel中的某行数据</param> /// <returns>不存在则返回null,存在则返回该项数据</returns> T IsExist(IRow row); /// <summary> /// 新数据添加到数据库 /// </summary> /// <param name="model">已完成映射的实体类实例</param> void Add(T model); /// <summary> /// 更新数据库中的已有数据 /// </summary> /// <param name="model">已完成映射的实体类实例</param> void Update(T model); /// <summary> /// 提供从表格行数据映射到实体类的方法(解析失败的时候直接要抛出异常) /// </summary> /// <param name="row">Excel中的某行数据</param> /// <param name="model">新建的或刚从数据库中读取到的实体类实例</param> /// <returns>映射失败或数据为空时返回false,则不会录入数据库</returns> bool Map(IRow row, T model); }
其他部分代码
/// <summary> /// 表格导入数据库 /// 也可以是导入DataTable等形式,只要提供不同的策略 /// </summary> /// <typeparam name="T">Model的类型</typeparam> public class ExcelImport<T> where T : new() { private ExcelImportHelper helper; /// <summary> /// 构造方法 /// </summary> /// <param name="helper">操作策略</param> public ExcelImport(ExcelImportHelper helper) { this.helper = helper; } /// <summary> /// 处理结果 /// </summary> public struct Result { private int num; //导入行数 private int num_add; //新增行数 private int num_edit; //修改行数 /// <summary> /// 导入行数 /// </summary> public int Num { get { return num; } set { num = value; } } /// <summary> /// 新增行数 /// </summary> public int Num_add { get { return num_add; } set { num_add = value; } } /// <summary> /// 修改行数 /// </summary> public int Num_edit { get { return num_edit; } set { num_edit = value; } } /// <summary> /// 构造函数 /// </summary> /// <param name="num">导入行数</param> /// <param name="num_add">新增行数</param> /// <param name="num_edit">修改行数</param> internal Result(int num, int num_add, int num_edit) { this.num = num; this.num_add = num_add; this.num_edit = num_edit; } } #region 导入操作 /// <summary> /// 表格导入到数据库 /// </summary> /// <param name="name">上传控件的name</param> /// <param name="maxLength">最大上传的文件大小(MB)</param> /// <param name="headerRowsNum">表格头部所占的高度</param> /// <returns>表中的数据(只读取表中第一个Sheet)</returns> public Result Inport(string name, int maxLength, int headerRowsNum) { Stream stream = null; try { HttpContext context = HttpContext.Current; HttpRequest request = context.Request; //验证文件格式大小,返回文件流 stream = GetInputStream(request, name, maxLength); //逐行读取文件内容并执行导入 return Operation(stream, headerRowsNum); } catch (Exception ex) { throw ex; } finally { if (stream != null) { stream.Close(); stream.Dispose(); } } } private Result Operation(Stream stream,int headerRowsNum) { //操作结果 Result result = new Result(0, 0, 0); //读取表格内容 try { using (stream) { //自动识别.xlsx(XSSFWorkbook),.xls(HSSFWorkbook) IWorkbook workbook = WorkbookFactory.Create(stream); if (workbook.NumberOfSheets == 0) throw new Exception("报错信息:文件无数据"); //取第一张表 ISheet sheet = workbook.GetSheetAt(0); if (sheet.PhysicalNumberOfRows == 0) throw new Exception("报错信息:文件无数据"); //数据行数 int rowCount = sheet.LastRowNum; //读取表中所有数据 for (int i = sheet.FirstRowNum + headerRowsNum; i <= rowCount; i++) { IRow row = sheet.GetRow(i); if (row != null) { result.Num++; T model = helper.IsExist(row); if (model == null) { result.Num_add++; //新建 model = new T(); bool ok = helper.Map(row, model); if (ok) { helper.Add(model); } } else { result.Num_edit++; //修改 bool ok = helper.Map(row, model); if (ok) { helper.Update(model); } } } } } return result; } catch (Exception ex) { //在异常信息中添加出错位置 throw new Exception("当前读取到第"+ result.Num + "行数据,已添加"+ result .Num_add+ "条,已更新" + result.Num_edit + "条," + ex.Message); } } #endregion }
调用
//策略 private class helper : ExcelImport<B_NEWS>.ExcelImportHelper { public void Add(B_NEWS model) { bll.Add(model); } public B_NEWS IsExist(IRow row) { return null; } public bool Map(IRow row, B_NEWS model) { model.id = row.GetValue<int>(0); model.c_info_title = row.GetValue<string>(1); model.c_info_content = row.GetValue<string>(2); model.d_list_date = row.GetValue<DateTime>(3); model.d_add_date = row.GetValue<DateTime>(4); model.n_is_active = (row.GetValue<bool>(5) ? 1 : 0); model.int1 = row.GetValue<int>(6); return true; } public void Update(B_NEWS model) { bll.Update(model); } } protected void daoru(object sender, EventArgs e) { new ExcelImport<B_NEWS>(new helper()).Inport("excel", 10, 2); }
表格导出
调用者直接接触的操作类
public class ExcelExport<T> { /// <summary> /// 每列数据的解析方法 /// </summary> private List<Func<T, object>> func = new List<Func<T, object>>(); private IExcelBuilder builder = new ExcelBuilder(); private string headJson; /// <summary> /// 更换表格建造器 /// </summary> /// <param name="builder">自定义的建造器</param> /// <returns></returns> public ExcelExport<T> ChangeBuilder(IExcelBuilder builder) { this.builder = builder; return this; } /// <summary> /// 定义列 /// </summary> /// <param name="convert">委托</param> /// <returns></returns> public ExcelExport<T> Column(Func<T, object> convert) { func.Add(convert); return this; } /// <summary> /// 设置Excel属性 /// </summary> /// <param name="ext"></param> /// <returns></returns> public ExcelExport<T> SetExcelProperty(ExcelProperty ext) { builder.SetWorkbook(ext); return this; } /// <summary> /// 设置表头 /// </summary> /// <param name="headJson">json形式的表头配置,可以配置多行表头</param> /// <returns></returns> public ExcelExport<T> SetExcelHeadJson(string headJson) { this.headJson = headJson; return this; } /// <summary> /// 设置表头 /// </summary> /// <param name="path">绝对路径,内里保存json形式的表头配置</param> /// <returns></returns> public ExcelExport<T> SetExcelHeadFromFile(string path) { //读取配置信息 this.headJson = File.ReadAllText(path); return this; } /// <summary> /// 设置表头 /// </summary> /// <param name="headStr">“序号|订单号|订单状态|客户收货情况反馈|商品评价”的形式,仅支持单行表头</param> /// <returns></returns> public ExcelExport<T> SetExcelHeadStr(string headStr) { string[] head = headStr.Split(\'|\'); string headJson = "{\\"root\\":{\\"head\\":["; for (int i = 0, length = head.Length; i < length; i++) { if (i > 0) { headJson += ","; } headJson += "{"; headJson += "\\"text\\":\\"" + head [i]+ "\\","; headJson += "\\"cell_region\\":\\"0,0," + i + "," + i + "\\""; headJson += "}"; } headJson += "]"; headJson += "}"; headJson += "}"; this.headJson = headJson; return this; } /// <summary> /// 导出WEB /// </summary> /// <param name="list">数据源(分组后的)</param> /// <param name="fileName">将要下载显示的名字</param> public void ExportToWeb(List<T> list, string fileName) { byte[] buffers = ForExport(list, fileName); ExportToWebExcel(buffers, fileName); } /// <summary> /// 导出到本地存储 /// </summary> /// <param name="list">数据源(分组后的)</param> /// <param name="fileName">文件名称,请自己包含路径,例如C:\\\\test.xls</param> public void ExportToLocal(List<T> list, string fileName) { byte[] buffers = ForExport(list, fileName); ExportToLocalExcel(buffers, fileName); } #region private private byte[] ForExport(List<T> list, string fileName) { Check(list); //构建表格 IWorkbook workbook = builder.BuildExcel<T>(list, headJson, func.ToArray()); return SaveToByteArray(workbook); } private void Check(List<T> list) { //表头不算必填项 //数据却不能为空 if (list.Count == 0) { throw new Exception("数据源不能为空"); } } /// <summary> /// 将WorkBook对象转换成内存流 /// </summary> /// <param name="wookbook"></param> /// <returns></returns> private byte[] SaveToByteArray(IWorkbook wookbook) { using (MemoryStream ms = new MemoryStream()) { wookbook.Write(ms); ms.Flush(); ms.Position = 0; return ms.GetBuffer(); } } /// <summary> /// 本地存储到excel /// </summary> /// <param name="buffers">文件二进制流</param> /// <param name="fileName">文件目录例如C:\\\\test.xls</param> private void ExportToLocalExcel(byte[] buffers, string fileName) { using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) { fs.Write(buffers, 0, buffers.Length); fs.Flush(); } } /// <summary> /// web导出excel /// </summary> /// <param name="buffers">文件二进制流</param> /// <param name="fileName">文件名称</param> private void ExportToWebExcel(byte[] buffers, string fileName) { HttpResponse Response = HttpContext.Current.Response; Response.BufferOutput = false; Response.ContentEncoding = Encoding.UTF8; Response.Charset = "utf-8"; Response.ContentType = "application/octet-stream"; Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName); Response.AddHeader("Content-Length", buffers.Length.ToString()); Response.Clear(); Response.BinaryWrite(buffers); Response.End(); } #endregion }
表格样式配置
/**为方便样式书写,属性统一用小写*/ /// <summary> /// 根节点, 定义Json表头的格式 /// </summary> public class JsonSetting { /// <summary> /// Root类 /// </summary> public HeadInfo root { get; set; } } #region HeadInfo /// <summary> /// 报表表格头部信息 /// </summary> public class HeadInfo { /// <summary> /// 表头的样式 /// </summary> public IList<Attribute4Head> head { get; set; } /// <summary> /// 每列单元格的样式 /// </summary> public IList<Attribute4Cell> cell { get; set; } /// <summary> /// 表名,默认sheet /// </summary> public string sheet_name { get; set; } /// <summary> /// 默认单元格宽度(所有列),不设置则自适应宽度 /// </summary> public int? default_width { get; set; } /// <summary> /// 默认行高度,不设置则使用表格的默认单元格高度 /// </summary> public int? default_height { get; set; } /// <summary> /// 表格边框颜色 例如#000000 /// </summary> public string border_color { get; set; } /// <summary> /// 边框风格,none,thin,dashed,dotted,double 默认 thin /// </summary> public string border_style { get; set; } /// <summary> /// 分组所在的列(索引从0开始:0,1) /// </summary> public string group_column { get; set; } } #endregion /// <summary> /// 关于表头单元格设置属性 /// </summary> public class Attribute4Head : CellAttribute { /// <summary> /// 单元格合并位置,(fromRow,toRow,fromColumn,toColumn) /// </summary> public string cell_region { get; set; } /// <summary> /// 显示的文字 /// </summary> public string text { get; set; } } /// <summary> /// 关于内容单元格设置属性 /// </summary> public class Attribute4Cell : CellAttribute { /// <summary> /// 数据格式(仅支持Excel的内嵌格式) /// </summary> public string data_format { get; set; } /// <summary> /// 宽度(内容单元格宽度覆盖默认宽度) /// </summary> public int? width { get; set; } } #region 公共属性 /// <summary> /// 公共属性 /// </summary> public abstract class CellAttribute { /// <summary> /// 高度(限定行高,一行中只要一个单元格给定高度就可以了,多个单元格都有配置高度时,取最后一次赋值) /// </summary> public int? height { get; set; } /// <summary> /// 水平对齐方式 center,left,right /// </summary> public string align { get; set; } /// <summary> /// 垂直对齐方式center,top,bottom /// </summary> public string vertical_align { get; set; } /// <summary> /// 背景颜色.例如#000000 /// </summary> public string background_color { get; set; } /// <summary> /// 前景颜色.例如#000000(单元格的背景色有bgcolor和forecolor两个背景色,一般使用forecolor) /// </summary> public string foreground_color { get; set; } /// <summary> /// 背景填充方式 /// </summary> public string fill_pattern { get; set; } /// <summary> /// 字体大小 /// </summary> public short? font_size { get; set; } /// <summary> /// 字体颜色 /// </summary> public string font_color { get; set; } /// <summary> /// 字体名称,默认微软雅黑 /// </summary> public string font_name { get; set; } /// <summary> ///是否是斜体 /// </summary> public bool? italic { get; set; } /// <summary> /// 是否有中间线 /// </summary> public bool? strike_out { get; set; } /// <summary> /// 是否设置下划线 /// </summary> public bool? underline { get; set; } /// <summary> /// 表头文字是否加粗 /// </summary> public bool? font_weight { get; set; } }
样式解析
/// <summary> /// 由字符样式读取相应的枚举值 /// </summary> /// <typeparam name="T">枚举类型</typeparam> /// <param name="str">字符串</param> /// <param name="defaultValue">默认值</param> /// <returns></returns> private T ToEnumValue<T>(string str,T defaultValue) { if (string.IsNullOrEmpty(str)) { return defaultValue; } //不区分大小写 str = str.Trim().ToLower(); if (str == "0") { //输入的是数值 return (T)Enum.Parse(typeof(T),"0"); } else { var fields = typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public); int val = str.ToInt(); if (val != 0 && val < fields.Length) { //输入的是数值 return (T)Enum.Parse(typeof(T), str); } else { //输入的是枚举名 foreach (var fi in fields) { var value = fi.GetValue(null); if(value.ToString().ToLower() == str) { return (T)value; } } return defaultValue; } } } /// <summary> /// 根据十六进制颜色获得颜色索引 /// </summary> /// <param name="workbook"></param> /// <param name="color"></param> /// <returns></returns> private short GetColorIndex(string color, HSSFWorkbook workbook) { Color colour = ColorTranslator.Fromhtml(color); if (this.XlPalette == null) { this.XlPalette = workbook.GetCustomPalette(); } short s = 0; HSSFColor XlColour = XlPalette.FindColor(colour.R, colour.G, colour.B); if (XlColour == null) { XlColour = XlPalette.FindSimilarColor(colour.R, colour.G, colour.B); s = XlColour.Indexed; } else { s = XlColour.Indexed; } return s; }
涉及到的枚举类型
涉及到的枚举类型 public enum FillPattern : short { NoFill = 0, SolidForeground = 1, FineDots = 2, AltBars = 3, SparseDots = 4, ThickHorizontalBands = 5, ThickVerticalBands = 6, ThickBackwardDiagonals = 7, ThickForwardDiagonals = 8, BigSpots = 9, Bricks = 10, ThinHorizontalBands = 11, ThinVerticalBands = 12, ThinBackwardDiagonals = 13, ThinForwardDiagonals = 14, Squares = 15, Diamonds = 16, LessDots = 17, LeastDots = 18 } public enum HorizontalAlignment { General = 0, Left = 1, Center = 2, Right = 3, Fill = 4, Justify = 5, CenterSelection = 6, Distributed = 7 } public enum VerticalAlignment { None = -1, Top = 0, Center = 1, Bottom = 2, Justify = 3, Distributed = 4 } public enum BorderStyle : short { None = 0, Thin = 1, Medium = 2, Dashed = 3, Dotted = 4, Thick = 5, Double = 6, Hair = 7, MediumDashed = 8, DashDot = 9, MediumDashDot = 10, DashDotDot = 11, MediumDashDotDot = 12, SlantedDashDot = 13 }
表格建造器
/// <summary> /// 表格建造器 /// </summary> public interface IExcelBuilder { /// <summary> /// 更换样式解析器 /// </summary> IExcelHelper Helper { set; } /// <summary> /// 构建表格 /// </summary> /// <typeparam name="T">泛型实体</typeparam> /// <param name="list">要转换的列表</param> /// <param name="headJson">json形式的表头配置</param> /// <param name="fieldFuncs">字段委托</param> /// <returns></returns> HSSFWorkbook BuildExcel<T>(List<T> list, string headJson, Func<T, object>[] fieldFuncs); /// <summary> /// 设置excel文件基本属性 /// </summary> /// <param name="ep"></param> void SetWorkbook(ExcelProperty ep); }
/// <summary> /// 构建表格 /// </summary> public class ExcelBuilder : IExcelBuilder { #region 字段及属性 private const int RowPerSheet = 65530; //每张sheet最多数据行数 private const int MaxColumnWidth = 50; //自适应列宽时的上限 private string SheetName //表名 { get { return Helper.SheetName; } } private int HeadRow //表头占据的行数 { get { return Helper.HeadRow; } } /// <summary> /// 样式解析器 /// </summary> public IExcelHelper Helper { set { this.helper = value; } private get { return helper; } } private HSSFWorkbook Workbook = new HSSFWorkbook(); //要导出的excel private IExcelHelper helper = new ExcelHelper(); //用于解析属性 private List<GroupClass> GroupColumn = new List<GroupClass>(); //分组的列的配置信息 private List<CellRangeAddress> CellRange = new List<CellRangeAddress>(); //要合并的单元格 private ISheet sheet = null; //要导出的excel对象中的一个表 #endregion #region IExcelBuilder /// <summary> /// 构建表格 /// </summary> /// <typeparam name="T">泛型实体</typeparam> /// <param name="list">要转换的列表</param> /// <param name="headJson">json形式的表头配置</param> /// <param name="fieldFuncs">字段委托</param> /// <returns></returns> public HSSFWorkbook BuildExcel<T>(List<T> list, string headJson, Func<T, object>[] fieldFuncs) { //获取第一行数据 List<object> dataRow = GetRowData(list[0], fieldFuncs); //解析第一行的数据,初步定下每列的默认样式 Helper.GetStyle(headJson,dataRow, fieldFuncs, Workbook, GroupColumn); //写入数据 Process(list, fieldFuncs); return Workbook; } /// <summary> /// 设置excel文件基本属性 /// </summary> /// <param name="ep"></param> public void SetWorkbook(ExcelProperty ep) { DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation(); dsi.Company = ep.Company; dsi.Manager = ep.Manager; dsi.Category = ep.Catagory; Workbook.DocumentSummaryInformation = dsi; SummaryInformation si = PropertySetFactory.CreateSummaryInformation(); si.Author = ep.Author; //填加xls文件作者信息 si.ApplicationName = ep.ApplicationName; //填加xls文件创建程序信息 si.LastAuthor = ep.LastAuthor; //填加xls文件最后保存者信息 si.Comments = ep.Comments; //填加xls文件作者信息 si.Title = ep.Title; //填加xls文件标题信息 si.Subject = ep.Subject; //填加文件主题信息 si.Keywords = ep.KeyWord; si.CreateDateTime = DateTime.Now; si.Comments = ep.Comments; Workbook.SummaryInformation = si; } #endregion #region 完成从数据到excel的转化 /// <summary> /// 整合数据 /// </summary> /// <typeparam name="T">泛型实体</typeparam> /// <param name="list">要转换的列表</param> /// <param name="fieldFuncs">字段委托</param> private void Process<T>(List<T> list, Func<T, object>[] fieldFuncs) { //内容行 int rowNum = -1; foreach (T item in list) { rowNum++; if ((rowNum % RowPerSheet) == 0) { //新建sheet NewSheet(rowNum); } //创建新行,获取数据 int rowIndex = rowNum % RowPerSheet + HeadRow; IRow row = sheet.CreateRow(rowIndex); if (Helper.CellHeight.HasValue) { //行高 row.HeightInPoints = Helper.CellHeight.Value; } List<object> dataRow = GetRowData(item, fieldFuncs); //写入 int maxIndex = dataRow.Count; for (int colIndex = 0; colIndex < maxIndex; colIndex++) { //bool,时间,字符串,数字 var val = dataRow[colIndex]; GroupClass groupClass = CheckGroup(colIndex); if (groupClass == null) { ICell cell = row.CreateCell(colIndex); WriteCell(row, cell, colIndex, val, fieldFuncs); } else { //分组 MergedRegionInColumn(groupClass, val, fieldFuncs, row, rowIndex, colIndex); } } } //单元格合并 foreach (var cellRange in CellRange) { sheet.AddMergedRegion(cellRange); //设置边框 ((HSSFSheet)sheet).SetEnclosedBorderOfRegion(cellRange, Helper.WholeBorderStyle, Helper.WholeBorderColor); } } private void NewSheet(int rowNum) { int sheetIndex = (rowNum / RowPerSheet) + 1; sheet = Workbook.CreateSheet(SheetName + sheetIndex); //表头 Helper.SetHeadAndDefaultStyle(Workbook, sheet); //新表新列 for (int i = 0, length = GroupColumn.Count; i < length; i++) { GroupColumn[i].Value = null; GroupColumn[i].Count = 0; GroupColumn[i].cellRange = null; } } private void MergedRegionInColumn<T>(GroupClass groupClass,object val, Func<T, object>[] fieldFuncs, IRow row, int rowIndex, int colIndex) { object LastRowValue = groupClass.Value; //比对上一行的数据 if (LastRowValue != null && LastRowValue.Equals(val)) { //相等则合并单元格,行数累计 //同一个单元格参与多次单元格合并,最终表格样式会出问题,需要先将旧的合并记录清掉 CellRangeAddress lastCellRange = groupClass.cellRange; if (lastCellRange != null) { CellRange.Remove(lastCellRange); } //新的合并配置 groupClass.Count++; CellRangeAddress cellRange = new CellRangeAddress(rowIndex - groupClass.Count, rowIndex, colIndex, colIndex); CellRange.Add(cellRange); groupClass.cellRange = cellRange; } else { //不等则更新后续分组的信息 for (int i = groupClass.Index, length = GroupColumn.Count; i < length; i++) { GroupColumn[i].Value = null; GroupColumn[i].Count = 0; groupClass.cellRange = null; } groupClass.Value = val; //创建新单元格,普通写入操作 ICell cell = row.CreateCell(colIndex); WriteCell(row, cell, colIndex, val, fieldFuncs); } } private void WriteCell<T>(IRow row, ICell cell,int colIndex,object val,Func<T, object>[] fieldFuncs) { ColumnInfo cellInfo = Helper.GetColumnInfo(colIndex); if (cellInfo == null) { cellInfo = Helper.AddColumn<T>(val, fieldFuncs, Workbook, sheet); } SetValue(cell, val); cell.CellStyle = cellInfo.Style; //调整列宽度 if (cellInfo.Width == ColumnInfo.Auto) { int len = Encoding.GetEncoding(936).GetByteCount(val + "") + 8; if (len > MaxColumnWidth) { len = MaxColumnWidth; } len = len * 256; if (sheet.GetColumnWidth(colIndex) < len) { sheet.SetColumnWidth(colIndex, len); } } } #region 取值与填值 /// <summary> /// 设置单元格的值 /// </summary> /// <param name="cell"></param> /// <param name="val"></param> private void SetValue(ICell cell, object val) { if (val is bool) { var data = val.ChangeType<bool>(); cell.SetCellValue(data); } else if (val is DateTime) { var data = val.ChangeType<DateTime>(); cell.SetCellValue(data); } else if (val is double || val is float || val is long || val is int || val is short || val is byte || val is decimal) { var data = val.ChangeType<double>(); cell.SetCellValue(data); } else { var data = val.ToString(); cell.SetCellValue(data); } } /// <summary> /// 解析数据 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="item"></param> /// <param name="fieldFuncs"></param> /// <returns></returns> private List<object> GetRowData<T>(T item, Func<T, object>[] fieldFuncs) { List<object> dataRow = new List<object>(); if (fieldFuncs.Length > 0) { for (int i = 0; i < fieldFuncs.Length; i++) { dataRow.Add(fieldFuncs[i](item)); } } else { if (item is DataRow) { var row = item as DataRow; foreach (DataColumn column in row.Table.Columns) { dataRow.Add(row[column]); } } else if (item is IEnumerable<T>) { foreach (var val in item as IEnumerable<T>) { dataRow.Add(val); } } else { throw new Exception("请添加从数据实例映射到表格行的方法"); } } return dataRow; } #endregion /// <summary> /// 判断当前操作的列是不是要分组 /// </summary> /// <param name="colIndex">列下表</param> /// <returns></returns> private GroupClass CheckGroup(int colIndex) { foreach (var item in GroupColumn) { if(item.Column == colIndex) { return item; } } return null; } #endregion }
表格样式解析器
/// <summary> /// 表格样式解析器 /// </summary> public interface IExcelHelper { /// <summary> /// 内容行的定高 /// </summary> int? CellHeight { get; } /// <summary> /// 表头占据的行数 /// </summary> int HeadRow { get; } /// <summary> /// 表名 /// </summary> string SheetName { get; } /// <summary> /// 整个表格border样式 /// </summary> short WholeBorderColor { get; } /// <summary> /// 整个表格border颜色 /// </summary> BorderStyle WholeBorderStyle { get; } /// <summary> /// 比第一行数据多出的列 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="val"></param> /// <param name="fieldFuncs"></param> /// <param name="workbook"></param> /// <param name="sheet"></param> ColumnInfo AddColumn<T>(object val, Func<T, object>[] fieldFuncs, HSSFWorkbook workbook, ISheet sheet); /// <summary> /// 读取列配置 /// </summary> /// <param name="index">列下标</param> /// <returns></returns> ColumnInfo GetColumnInfo(int index); /// <summary> /// 计算每一列的默认单元格样式 /// </summary> void GetStyle<T>(string headJson, List<object> dataRow, Func<T, object>[] fieldFuncs, HSSFWorkbook workbook, List<GroupClass> groupList); /// <summary> /// 为新sheet设置表头和默认样式 /// </summary> /// <param name="workbook"></param> /// <param name="sheet"></param> void SetHeadAndDefaultStyle(HSSFWorkbook workbook, ISheet sheet); }
/// <summary> /// 解析表格样式 /// </summary> public class ExcelHelper : IExcelHelper { #region 字段及属性 private IList<Attribute4Head> CellStyle4Head = null; //头部的配置信息 private IList<Attribute4Cell> CellStyle4Cell = null; //列配置信息 private IList<ColumnInfo> columnInfo = new List<ColumnInfo>(); //记录解析后得到的每列的宽度及样式 private string sheetName; //表名 private int headRow = 1; //表头占据的行数 private int CellDefaultWidth; //单元格宽度(默认值为ColumnInfo.Auto) private int? RowDefaultHeight; //默认单元格高度(内容多了会自动撑高) private int? cellHeight; //内容行的定高 private BorderStyle wholeBorderStyle; //整个表格border样式,默认solid(thin) private short wholeBorderColor; //整个表格border颜色 private HSSFPalette XlPalette = null; //用于自定义颜色的对象(GetSetting中创建) /// <summary> /// 表名 /// </summary> public string SheetName { get { return sheetName; } } /// <summary> /// 表头占据的行数 /// </summary> public int HeadRow { get { return headRow; } } /// <summary> /// 内容行的定高 /// </summary> public int? CellHeight { get { return cellHeight; } } /// <summary> /// 整个表格border样式 /// </summary> public BorderStyle WholeBorderStyle { get { return wholeBorderStyle; } } /// <summary> /// 整个表格border颜色 /// </summary> public short WholeBorderColor { get { return wholeBorderColor; } } #endregion #region 列配置操作 /// <summary> /// 比第一行数据多出的列 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="val"></param> /// <param name="fieldFuncs"></param> /// <param name="workbook"></param> /// <param name="sheet"></param> public ColumnInfo AddColumn<T>(object val, Func<T, object>[] fieldFuncs, HSSFWorkbook workbook, ISheet sheet) { ColumnInfo info = GetDefaultCellStyle(val, fieldFuncs, workbook); columnInfo.Add(info); int index = columnInfo.Count - 1; UpdateColumn(index, workbook); //设置列宽 if (info.Width != ColumnInfo.Auto) { sheet.SetColumnWidth(index, info.Width * 256); } return info; } /// <summary> /// 依据配置更新列配置 /// </summary> /// <param name="index">列下标</param> /// <param name="workbook"></param> private void UpdateColumn(int index,HSSFWorkbook workbook) { ColumnInfo columnInfo = GetColumnInfo(index); if (columnInfo != null) { //列宽先赋值一个默认宽度 columnInfo.Width = this.CellDefaultWidth; ICellStyle cellStyle = columnInfo.Style; //设置单元格边框 cellStyle.BorderRight = cellStyle.BorderLeft = cellStyle.BorderBottom = cellStyle.BorderTop = WholeBorderStyle; cellStyle.BottomBorderColor = cellStyle.RightBorderColor = cellStyle.LeftBorderColor = cellStyle.TopBorderColor = WholeBorderColor; //更新样式 Attribute4Cell style = null; if (CellStyle4Cell != null && CellStyle4Cell.Count > index) { style = CellStyle4Cell[index]; } if (style != null) { //数据格式 if (!string.IsNullOrEmpty(style.data_format)) { cellStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat(style.data_format); } //列宽 if (style.width.HasValue) { columnInfo.Width = style.width.Value; } //高度 if (style.height.HasValue) { this.cellHeight = style.height.Value; } //其他属性 AdaperCellStyle(cellStyle, style, workbook); } } } /// <summary> /// 读取列配置 /// </summary> /// <param name="index">列下标</param> /// <returns></returns> public ColumnInfo GetColumnInfo(int index) { if (columnInfo.Count > index) { return columnInfo[index]; } return null; } #endregion #region 解析样式 /// <summary> /// 计算每一列的默认单元格样式 /// </summary> public void GetStyle<T>(string headJson, List<object> dataRow, Func<T, object>[] fieldFuncs, HSSFWorkbook workbook, List<GroupClass> groupList) { // 读取第一行数据,为每列解析默认样式,依序加入cellStyle4Cell GetDefaultCellStyle(dataRow, fieldFuncs, workbook); //解析headJson GetSetting(headJson, workbook, groupList); } /// <summary> /// 依据一行示例数据为每列解析默认样式,依序加入cellStyle4Cell /// </summary> /// <typeparam name="T"></typeparam> /// <param name="dataRow"></param> /// <param name="fieldFuncs"></param> /// <param name="workbook"></param> private void GetDefaultCellStyle<T>(List<object> dataRow, Func<T, object>[] fieldFuncs, IWorkbook workbook) { for (int colIndex = 0; colIndex < dataRow.Count; colIndex++) { var val = dataRow[colIndex]; ColumnInfo info = GetDefaultCellStyle(val, fieldFuncs, workbook); columnInfo.Add(info); } } /// <summary> /// //依据每个单元格对应的数据的数据类型计算出每列的默认样式,列的宽度设为自动 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="val"></param> /// <param name="fieldFuncs"></param> /// <param name="workbook"></param> /// <returns></returns> private ColumnInfo GetDefaultCellStyle<T>(object val, Func<T, object>[] fieldFuncs, IWorkbook workbook) { ICellStyle cellStyle; if (val is bool) { cellStyle = GetCellStyle(workbook, "默认"); } else if (val is DateTime) { var data = val.ChangeType<DateTime>(); if (data.Hour == 0 && data.Minute == 0 && data.Second == 0) { cellStyle = GetCellStyle(workbook, "时间"); } else { cellStyle = GetCellStyle(workbook, "时间2"); } } else if (val is double || val is float || val is long || val is int || val is short || val is byte || val is decimal) { var data = val.ChangeType<double>(); if ((val + "").IndexOf(".") > 0) { cellStyle = GetCellStyle(workbook, "数字"); } else { cellStyle = GetCellStyle(workbook, "数字2"); } } else { cellStyle = GetCellStyle(workbook, "默认"); } return new ColumnInfo() { Style = cellStyle, Width = ColumnInfo.Auto }; } /// <summary> /// 解析json配置 /// </summary> /// <param name="headJson">json字符串</param> /// <param name="workbook"></param> /// <param name="groupList"></param> private void GetSetting(string headJson, HSSFWorkbook workbook, List<GroupClass> groupList) { JsonSetting T = headJson.ToObject<JsonSetting>(); //DebugUtils.Log(new MT.Business.Json().Convert2Json(T)); //========== //分组的列信息 GetGroupCellSetting(T.root.group_column, groupList); //表名 this.sheetName = T.root.sheet_name; if (string.IsNullOrEmpty(this.sheetName)) { this.sheetName = "sheet"; } //默认单元格高度 this.RowDefaultHeight = T.root.default_height; //边框样式 this.wholeBorderStyle = ToEnumValue<BorderStyle>(T.root.border_style, BorderStyle.Thin); //边框颜色 this.wholeBorderColor = GetColorIndex(T.root.border_color, workbook); //表头样式 CellStyle4Head = T.root.head; //单元格样式 CellStyle4Cell = T.root.cell; //默认单元格宽度 this.CellDefaultWidth = (T.root.default_width.HasValue ? T.root.default_width.Value : ColumnInfo.Auto); //修正每列的配置信息 for (int i = 0, length = this.columnInfo.Count; i < length; i++) { UpdateColumn(i, workbook); } } /// <summary> /// 初始化分组统计 /// </summary> private void GetGroupCellSetting(string groupColumn, List<GroupClass> list) { int[] group = (string.IsNullOrEmpty(groupColumn) ? null : ToIntArray(groupColumn.Split(\',\'))); if (group != null) { //依列下标排序 group = group.OrderBy(x => x).ToArray(); for (int i = 0, length = group.Length; i < length; i++) { list.Add(new GroupClass { Column = group[i], Count = 0, Index = i }); } } } #endregion #region 为新sheet设置表头和默认样式等 /// <summary> /// 设置表头和默认样式 /// </summary> /// <param name="workbook"></param> /// <param name="sheet"></param> public void SetHeadAndDefaultStyle(HSSFWorkbook workbook, ISheet sheet) { sheet.DisplayGridlines = true; if(this.CellDefaultWidth != ColumnInfo.Auto) { //默认单元格宽度 sheet.DefaultColumnWidth = this.CellDefaultWidth; } if (this.RowDefaultHeight.HasValue) { //设置表格默认行高 sheet.DefaultRowHeight = (short)(this.RowDefaultHeight.Value * 20); } //给定的列宽 for (int i = 0, length = this.columnInfo.Count; i < length; i++) { var info = this.columnInfo[i]; if (info.Width != ColumnInfo.Auto) { sheet.SetColumnWidth(i, info.Width * 256); } } //写入表头 SetHead(workbook,sheet); } /// <summary> /// 设置表头 /// </summary> /// <param name="workbook"></param> /// <param name="sheet"></param> private void SetHead(HSSFWorkbook workbook, ISheet sheet) { List<IRow> rowList = new List<IRow>(); //创建行 for (int i = 0; i < HeadRow; i++) { IRow temp = sheet.CreateRow(i); rowList.Add(temp); } for (int i = 0; i < CellStyle4Head.Count; i++) { Attribute4Head attribute4Head = CellStyle4Head[i]; int[] c = ToIntArray(attribute4Head.cell_region.Split(\',\')); //计算title要插入的位置的索引 int txtIndex = -1; int txtRow = -1; if ((c[0] <= c[1] && c[2] <= c[3])) { #region 写入表头 //表头所占的列数 int _headRow = c[1] + 1; if (headRow<= _headRow) { //创建行 for (int j = headRow; j < _headRow; j++) { IRow temp = sheet.CreateRow(j); rowList.Add(temp); } headRow = _headRow; } //@ 合并单元格 if (c[0] < c[1] || c[2] < c[3]) { //例如1,1,2,2 第二行中的第3列,例如1,1,2,7 第二行中的(第3列到8列),合并列 CellRangeAddress cellRange = new CellRangeAddress(c[0], c[1], c[2], c[3]); sheet.AddMergedRegion(cellRange); //设置边框 ((HSSFSheet)sheet).SetEnclosedBorderOfRegion(cellRange, WholeBorderStyle, WholeBorderColor); } //@ 填充内容 txtIndex = c[2]; txtRow = c[0]; ICell cell1 = rowList[txtRow].CreateCell(txtIndex); cell1.SetCellValue(attribute4Head.text); cell1.CellStyle = GetHeadCellStyle(workbook, attribute4Head); //高度 if (attribute4Head.height.HasValue) { rowList[txtRow].HeightInPoints = attribute4Head.height.Value; } #endregion } } } private ICellStyle GetHeadCellStyle(HSSFWorkbook workbook,Attribute4Head cellStyle) { ICellStyle headStyle = workbook.CreateCellStyle(); //设置单元格边框 headStyle.BorderRight = headStyle.BorderLeft = headStyle.BorderBottom = headStyle.BorderTop = WholeBorderStyle; headStyle.BottomBorderColor = headStyle.RightBorderColor = headStyle.LeftBorderColor = headStyle.TopBorderColor = WholeBorderColor; //默认样式 //水平对齐 headStyle.Alignment = HorizontalAlignment.Center; //垂直对齐 headStyle.VerticalAlignment = VerticalAlignment.Center; IFont font = workbook.CreateFont(); //字体大小 font.FontHeightInPoints = 12; //字体颜色 font.Color = 8; //默认黑色 //字体名称 font.FontName = "微软雅黑"; //加粗 font.Boldweight = (short)FontBoldWeight.Bold; headStyle.SetFont(font); AdaperCellStyle(headStyle, cellStyle, workbook); return headStyle; } #endregion #region 字符串To 样式 /// <summary> /// 由字符样式读取响应的枚举值 /// </summary> /// <typeparam name="T">枚举类型</typeparam> /// <param name="str">字符串</param> /// <param name="defaultValue">默认值</param> /// <returns></returns> private T ToEnumValue<T>(string str,T defaultValue) { if (string.IsNullOrEmpty(str)) { return defaultValue; } //不区分大小写 str = str.Trim().ToLower(); if (str == "0") { //输入的是数值 return (T)Enum.Parse(typeof(T),"0"); } else { var fields = typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public); int val = str.ToInt(); if (val != 0 && val < fields.Length) { //输入的是数值 return (T)Enum.Parse(typeof(T), str); } else { //输入的是枚举名 foreach (var fi in fields) { var value = fi.GetValue(null); if(value.ToString().ToLower() == str) { return (T)value; } } return defaultValue; } } } /// <summary> /// 根据十六进制颜色获得颜色索引 /// </summary> /// <param name="workbook"></param> /// <param name="color"></param> /// <returns></returns> private short GetColorIndex(string color, HSSFWorkbook workbook) { Color colour = ColorTranslator.FromHtml(color); if (this.XlPalette == null) { this.XlPalette = workbook.GetCustomPalette(); } short s = 0; HSSFColor XlColour = XlPalette.FindColor(colour.R, colour.G, colour.B);以上是关于NPOI导入,导出的主要内容,如果未能解决你的问题,请参考以下文章