当前位置: 首页 > 工具软件 > Npoi.Mapper > 使用案例 >

使用NPOI、泛型、反射实现的通用excel帮助类

糜博远
2023-12-01

现在的项目基本都会使用一些主流的orm框架,几乎不会再用到datable

所以个人使用npoi、泛型、反射写了一个适用于主流orm框架的excel导入、导出帮助类,可以根据实体自动反射出需要导入导出的列名,导入excel时直接生成List<T>

可使用ef、dapper等orm框架直接批量插入List<T>

使用ado.net  的话个人还是觉得写datatable做批量导入好一点

第一步:nuget包安装npoi

第二步:新建ExcelHelper类:

然后直接上代码:

using NPOI.HSSF.UserModel;
using NPOI.HSSF.Util;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using R.Common.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web;

namespace R.Common.Excel
{
    public class ExcelHelper<T> where T : new()
    {
        /// <summary>  
        /// List<T>导出到Excel的MemoryStream  
        /// </summary>  
        /// <param name="list">导出数据</param>  
        /// <param name="titleText">表头文本</param>  
        /// <param name="columns">需要导出的属性</param>  
        /// <param name="columns">设置某些列的宽度</param>  
        public static MemoryStream ExportToExcel(List<T> list, string titleText, string[] columns, Dictionary<int, int> colWidthDic)
        {
            HSSFWorkbook workbook = new HSSFWorkbook();
            ISheet sheet = workbook.CreateSheet();
            try
            {
                Type type = typeof(T);
                PropertyInfo[] properties = ReflexHelper.GetProperties(type, columns);

                ICellStyle dateStyle = workbook.CreateCellStyle();
                dateStyle.BorderBottom = BorderStyle.Thin;
                dateStyle.BorderLeft = BorderStyle.Thin;
                dateStyle.BorderRight = BorderStyle.Thin;
                dateStyle.BorderTop = BorderStyle.Thin;
                dateStyle.BottomBorderColor = HSSFColor.Black.Index;
                dateStyle.LeftBorderColor = HSSFColor.Black.Index;
                dateStyle.RightBorderColor = HSSFColor.Black.Index;
                dateStyle.TopBorderColor = HSSFColor.Black.Index;
                dateStyle.VerticalAlignment = VerticalAlignment.Top;
                IDataFormat format = workbook.CreateDataFormat();
                dateStyle.DataFormat = format.GetFormat("yyyy-MM-dd");
                dateStyle.Alignment = HorizontalAlignment.CenterSelection;

                #region 获取每列的列宽(最大宽度)
                int[] arrColWidth = new int[properties.Length];
                for (int columnIndex = 0; columnIndex < properties.Length; columnIndex++)
                {
                    arrColWidth[columnIndex] = properties[columnIndex].Name.Length; // GBK对应的code page是CP936
                }

                #endregion
                for (int rowIndex = 0; rowIndex < list.Count; rowIndex++)
                {
                    #region 新建sheet页,填充表头,填充列头,样式
                    if (rowIndex == 65535 || rowIndex == 0)
                    {
                        if (rowIndex != 0)
                            sheet = workbook.CreateSheet();
                        #region 表头及样式
                        {
                            IRow headerRow = sheet.CreateRow(0);
                            headerRow.HeightInPoints = 25;
                            headerRow.CreateCell(0).SetCellValue(titleText);

                            ICellStyle headStyle = workbook.CreateCellStyle();
                            headStyle.Alignment = HorizontalAlignment.Center;
                            IFont font = workbook.CreateFont();
                            font.FontHeightInPoints = 20;
                            font.Boldweight = 700;
                            headStyle.SetFont(font);

                            headerRow.GetCell(0).CellStyle = headStyle;

                            sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, properties.Length - 1));
                        }
                        #endregion

                        #region 列头及样式
                        {
                            IRow headerRow = sheet.CreateRow(1);
                            ICellStyle headStyle = workbook.CreateCellStyle();
                            headStyle.Alignment = HorizontalAlignment.Center;

                            headStyle.BorderBottom = BorderStyle.Thin;
                            headStyle.BorderLeft = BorderStyle.Thin;
                            headStyle.BorderRight = BorderStyle.Thin;
                            headStyle.BorderTop = BorderStyle.Thin;
                            headStyle.BottomBorderColor = HSSFColor.Black.Index;
                            headStyle.LeftBorderColor = HSSFColor.Black.Index;
                            headStyle.RightBorderColor = HSSFColor.Black.Index;
                            headStyle.TopBorderColor = HSSFColor.Black.Index;
                            headStyle.Alignment = HorizontalAlignment.Center;

                            IFont font = workbook.CreateFont();
                            font.FontHeightInPoints = 10;
                            font.Boldweight = 700;
                            headStyle.SetFont(font);

                            for (int columnIndex = 0; columnIndex < properties.Length; columnIndex++)
                            {
                                // 类属性如果有Description就用Description当做列名
                                DescriptionAttribute customAttribute = (DescriptionAttribute)Attribute.GetCustomAttribute(properties[columnIndex], typeof(DescriptionAttribute));
                                string description = properties[columnIndex].Name;
                                if (customAttribute != null)
                                    description = customAttribute.Description;

                                headerRow.CreateCell(columnIndex).SetCellValue(description);
                                headerRow.GetCell(columnIndex).CellStyle = headStyle;
                            }
                        }
                        #endregion
                    }
                    #endregion

                    #region 填充内容
                    IRow dataRow = sheet.CreateRow(rowIndex + 2); // 前面2行已被占用
                    for (int columnIndex = 0; columnIndex < properties.Length; columnIndex++)
                    {
                        ICellStyle contentStyle = workbook.CreateCellStyle();
                        contentStyle.BorderBottom = BorderStyle.Thin;
                        contentStyle.BorderLeft = BorderStyle.Thin;
                        contentStyle.BorderRight = BorderStyle.Thin;
                        contentStyle.BorderTop = BorderStyle.Thin;
                        contentStyle.BottomBorderColor = HSSFColor.Black.Index;
                        contentStyle.LeftBorderColor = HSSFColor.Black.Index;
                        contentStyle.RightBorderColor = HSSFColor.Black.Index;
                        contentStyle.TopBorderColor = HSSFColor.Black.Index;
                        contentStyle.VerticalAlignment = VerticalAlignment.Top;
                        //contentStyle.WrapText = true;//是否换行                    
                        contentStyle.Alignment = HorizontalAlignment.Center;//居中
                        //contentStyle.Alignment = HorizontalAlignment.Left; //靠左

                        int columnWidth = sheet.GetColumnWidth(columnIndex) / 256;
                        for (int rowNum = 1; rowNum <= sheet.LastRowNum; rowNum++)
                        {
                            IRow currentRow;
                            //当前行未被使用过
                            if (sheet.GetRow(rowNum) == null)
                                currentRow = sheet.CreateRow(rowNum);
                            else
                                currentRow = sheet.GetRow(rowNum);

                            string widthValue = string.Empty;
                            if (properties[columnIndex].GetValue(list[rowIndex], null) != null)
                                widthValue = properties[columnIndex].GetValue(list[rowIndex], null).ToString();

                            if (currentRow.GetCell(columnIndex) != null)
                            {
                                ICell currentCell = currentRow.GetCell(columnIndex);
                                int length = Encoding.Default.GetBytes(widthValue).Length;
                                if (columnWidth < length)
                                    columnWidth = length;
                            }
                        }

                        if (colWidthDic != null && colWidthDic.ContainsKey(columnIndex))
                            colWidthDic.TryGetValue(columnIndex, out columnWidth); //设置某些特定列的宽度

                        //设置列宽  
                        sheet.SetColumnWidth(columnIndex, columnWidth * 256);
                        ICell newCell = dataRow.CreateCell(columnIndex);

                        newCell.CellStyle = contentStyle;
                        string drValue = "";
                        if (properties[columnIndex].GetValue(list[rowIndex], null) != null)
                        {
                            drValue = properties[columnIndex].GetValue(list[rowIndex], null).ToString();
                        }

                        switch (properties[columnIndex].PropertyType.ToString())
                        {
                            case "System.String":
                                newCell.SetCellValue(drValue);
                                break;

                            case "System.DateTime":
                            case "System.Nullable`1[System.DateTime]":
                                newCell.SetCellValue(drValue.ParseToDateTime());
                                newCell.CellStyle = dateStyle; //格式化显示  
                                break;

                            case "System.Boolean":
                            case "System.Nullable`1[System.Boolean]":
                                newCell.SetCellValue(drValue.ParseToString());
                                break;

                            case "System.Byte":
                            case "System.Nullable`1[System.Byte]":
                            case "System.Int16":
                            case "System.Nullable`1[System.Int16]":
                            case "System.Int32":
                            case "System.Nullable`1[System.Int32]":
                                newCell.SetCellValue(drValue.ParseToInt());
                                break;

                            case "System.Int64":
                            case "System.Nullable`1[System.Int64]":
                                newCell.SetCellValue(drValue.ParseToString());
                                break;

                            case "System.Double":
                            case "System.Nullable`1[System.Double]":
                                newCell.SetCellValue(drValue.ParseToDouble());
                                break;

                            case "System.Decimal":
                            case "System.Nullable`1[System.Decimal]":
                                newCell.SetCellValue(drValue.ParseToDouble());
                                break;

                            case "System.DBNull":
                                newCell.SetCellValue(string.Empty);
                                break;

                            default:
                                newCell.SetCellValue(string.Empty);
                                break;
                        }
                    }
                    #endregion
                }
            }
            catch (Exception ex)
            {
                workbook.Clear();
                throw ex;
            }

            MemoryStream file = new MemoryStream();
            workbook.Write(file);            
            return file;
        }

        /// <summary>
        /// 导入excel
        /// </summary>
        /// <param name="httpPostedFileBase">excel文件</param>
        /// <param name="sheetName">sheet名称</param>
        /// <param name="headerRowIndex">表头所在行下标</param>
        /// <param name="startRowIndex">数据开始行下标</param>
        /// <param name="sheetIndex">sheet页下标</param>
        /// <returns>List<T></returns>
        public static List<T> GetExcelData(HttpPostedFileBase httpPostedFileBase, out string errMsg, string sheetName = null, int headerRowIndex = 0, int startRowIndex = 1, int sheetIndex = 0)
        {
            List<T> dataList = new List<T>();// 定义集合            
            Stream fileStream = httpPostedFileBase.InputStream;
            IWorkbook Workbook;
            int FileLen = httpPostedFileBase.ContentLength;

            byte[] bytes = new byte[FileLen];

            fileStream.Read(bytes, 0, FileLen); // 读取文件的 byte[]   

            string fileExt = httpPostedFileBase.FileName.Substring(httpPostedFileBase.FileName.LastIndexOf(".") + 1);
            if (fileExt == "xls")
                Workbook = new HSSFWorkbook(fileStream);       //HSSFWorkbook 适用XLS格式
            else if (fileExt == "xlsx")
                Workbook = WorkbookFactory.Create(fileStream);      //XSSFWorkbook 适用XLSX格式     
            else
            {
                errMsg = "请上传.xls/.xlsx格式文件,导入失败!!!";
                return null;
            }

            try
            {
                //默认定位在第一个sheet; sheet页下标大于0 则获取对应sheet页下标数据
                ISheet sheet = Workbook.GetSheetAt(sheetIndex);
                if (!string.IsNullOrEmpty(sheetName))       // sheet页名称不为空 则获取对应sheet页数据
                    sheet = Workbook.GetSheet(sheetName);
                //第一行为标题行
                IRow headerRow = sheet.GetRow(headerRowIndex);

                int cellCount = headerRow.LastCellNum; // 总列数
                int rowCount = sheet.LastRowNum; // 总行数

                List<string> colNameArr = new List<string>();
                循环添加标题列
                for (int i = headerRow.FirstCellNum; i < cellCount; i++)
                {
                    colNameArr.Add(headerRow.GetCell(i).StringCellValue);
                }

                //把数据添加到dataList
                for (int i = startRowIndex; i <= rowCount; i++)
                {
                    IRow row = sheet.GetRow(i);
                    T tEntity = new T();
                    PropertyInfo[] propertys = GetColNameList(tEntity.GetType().GetProperties(), colNameArr, cellCount);// 获得此模型的公共属性
                    if (row != null)
                    {
                        for (int j = row.FirstCellNum; j < cellCount; j++)
                        {
                            var cellVal = row.GetCell(j);
                            if (cellVal != null)
                            {
                                var objVal = GetCellValue(cellVal);
                                if (cellVal.CellType == CellType.Numeric && propertys[j].PropertyType.ToString() == "System.String")
                                    propertys[j].SetValue(tEntity, objVal.ToString(), null);
                                else
                                    propertys[j].SetValue(tEntity, objVal, null);
                            }
                        }
                    }
                    dataList.Add(tEntity);
                }
                errMsg = "数据导入成功!!!";
            }
            catch (Exception ex)
            {
                errMsg = ex.ToString() + ",数据导入失败!!!";
            }
            finally
            {
                fileStream.Close();
                Workbook.Clear();
            }
            return dataList;
        }

        private static object  GetCellValue(ICell cell)
        {
            if (cell == null)
                return string.Empty;

            switch (cell.CellType)
            {
                case CellType.Blank:
                    return string.Empty;
                case CellType.Boolean:
                    return cell.BooleanCellValue;
                case CellType.Error:
                    return cell.ErrorCellValue.ToString();
                case CellType.Numeric:
                    if (DateUtil.IsCellDateFormatted(cell))
                        return cell.DateCellValue;
                    else
                        return cell.NumericCellValue;
                case CellType.Unknown:
                default:
                    return cell.ToString();
                case CellType.String:
                    return cell.StringCellValue;                
                case CellType.Formula:
                    try
                    {
                        HSSFFormulaEvaluator e = new HSSFFormulaEvaluator(cell.Sheet.Workbook);
                        e.EvaluateInCell(cell);
                        return cell.ToString();
                    }
                    catch
                    {
                        return cell.NumericCellValue.ToString();
                    }
            }
        }
        /// <summary>
        /// 反射出有添加Description所有属性
        /// </summary>
        /// <param name="propertys">实体的所以属性列表</param>
        /// <param name="colNameArr">需要导入的表字段</param>
        /// <param name="cellCount">导入字段数量</param>
        /// <returns></returns>
        private static PropertyInfo[] GetColNameList(PropertyInfo[] propertys, List<string> colNameArr, int cellCount)
        {
            PropertyInfo[] propertyList = new PropertyInfo[cellCount];            
            foreach (PropertyInfo pi in propertys)
            {
                //校验实体属性是否有添加Description
                DescriptionAttribute entityAttribute = (DescriptionAttribute)Attribute.GetCustomAttribute(pi, typeof(DescriptionAttribute)); 
                string colName = pi.Name;
                if (entityAttribute != null)                
                    colName = entityAttribute.Description;

                if (colNameArr.Contains(colName)) //判断属性是否在导入列表中
                {
                    for (int i = 0; i < colNameArr.Count; i++)
                    {
                        if (colNameArr[i] == colName)
                            propertyList[i] = pi; //属性在列表中则添加到数组
                    }
                }         
            }

            return propertyList;
        }

    }
}

个人测试了一下性能,这个方案比写datatable要快一丢丢!!!!!!!!!!
有需要的老铁直接复制到项目中即可,有更好的方案也希望各位老铁们分享给我学习学习,谢谢!!!

 类似资料: