【EXCEL终极总结分享】基于NPOI扩展封装的简易操作工具类库(简单灵活易用,支持导出、导入、上传等常见操作)

对于EXCEL的导入、导出,我之前已分享过多次,比如:

第一种方案:《我写的一个ExcelHelper通用类,可用于读取或生成数据》这个主要是利用把EXCEL当成一个DB来进行获取数据,导出则是采用拼接成HTML TABLE的方式,这种在ASP.NET,ASP.NET CORE 中也是可以的,简单方便,依赖Office Excel组件,仅适用于网站服务端。 推荐指数:

第二种方案:《MVC导出数据到EXCEL新方法:将视图或分部视图转换为HTML后再直接返回FileResult》这个主要是实现导出EXCEL的方法,依赖MVC框架,利用视图引擎解析渲染View(view也主要是HTML表格模板)获得HTML TABLE,本质与第一种的导出相同,只是这里不用代码去拼接HTML TABLE而是写View HTML模板即可,简单方便,适用于ASP.NET MVC、ASP.NET MVC CORE; 推荐指数:

第三种方案:《NPOI导入导出EXCEL通用类,供参考,可直接使用在WinForm项目中》这个实现了EXCEL的导入、导出方法,依赖NPOI,优点是:无需安装EXCEL也能操作EXCEL,文中的实现方案及示例主要是用在C/S中,对B/S 支持不够友好,当然可以进行适当修改就能兼容WEB版。推荐指数:

第四种方案:《分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库》这个完美的灵活实现了EXCEL的各种导入、导出方法,支持基于模板导出,格式自定义等 特点,依赖:ExcelReport、NPOI、NPOI.Extend,支持C/S、B/S(C/S特有的方法除外),适用于企业级EXCEL操作比较复杂的场景:ExcelUtility,推荐指数:♥,git地址:https://gitee.com/zuowj/ExcelUtility

第五种方案:就是本文即将介绍的,依赖NPOI,实现EXCEL的导入、导出、上传等常见方法,充分借鉴第三种、第四种方案,抽出核心的实现方法,并进行适当改造,以满足绝大部份的EXCEL场景,轻量、简单、易用,支持链式操作,类库名:ExcelEasyUtil,故名思义:EXCEL简单实用工具类库。

该类库主要是基于NPOI的IWorkbook进行扩展,扩展方法实现了常见的几种导出EXCEL、导入EXCEL数据的方法:基于类型集合,基于DataTable,NPOIExtensions完整实现代码如下:

using Newtonsoft.Json.Linq;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq.Expressions;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;

namespace ExcelEasyUtil
{

    /// <summary>
    /// NPOI扩展类
    /// author:zuowenjun
    /// 2019-5-21
    /// </summary>
    public static class NPOIExtensions
    {
        /// <summary>
        /// 将一个实体数据对象填充到一个EXCEL工作表中(可连续填充多个sheet,如:FillSheet(...).FillSheet(..) )
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="book"></param>
        /// <param name="sheetName"></param>
        /// <param name="headerColNames"></param>
        /// <param name="excelData"></param>
        /// <param name="getCellValueFunc"></param>
        /// <returns></returns>
        public static IWorkbook FillSheet<T>(this IWorkbook book, string sheetName, IList<T> excelData,
            IList<string> headerColNames, Func<T, List<object>> getCellValuesFunc, IDictionary<string, string> colDataFormats = null) where T : class
        {
            var sheet = book.CreateSheet(sheetName);

            IRow rowHeader = sheet.CreateRow(0);
            var headerCellStyle = GetCellStyle(book, true);
            Dictionary<int, ICellStyle> colStyles = new Dictionary<int, ICellStyle>();
            List<Type> colTypes = new List<Type>();
            Type strType = typeof(string);
            for (int i = 0; i < headerColNames.Count; i++)
            {
                ICell headerCell = rowHeader.CreateCell(i);
                headerCell.CellStyle = headerCellStyle;

                string colName = headerColNames[i];

                if (colName.Contains(":"))
                {
                    var colInfos = colName.Split(‘:‘);
                    colName = colInfos[0];
                    colTypes.Add(GetColType(colInfos[1]));
                }
                else
                {
                    colTypes.Add(strType);
                }

                headerCell.SetCellValue(colName);
                if (colDataFormats != null && colDataFormats.ContainsKey(colName))
                {
                    colStyles[i] = GetCellStyleWithDataFormat(book, colDataFormats[colName]);
                }
                else
                {
                    colStyles[i] = GetCellStyle(book);
                }
            }

            //生成excel内容
            for (int i = 0; i < excelData.Count; i++)
            {
                IRow irow = sheet.CreateRow(i + 1);
                var row = excelData[i];
                var cellValues = getCellValuesFunc(row);
                for (int j = 0; j < headerColNames.Count; j++)
                {
                    ICell cell = irow.CreateCell(j);
                    string cellValue = string.Empty;
                    if (cellValues.Count - 1 >= j && cellValues[j] != null)
                    {
                        cellValue = cellValues[j].ToString();
                    }

                    SetCellValue(cell, cellValue, colTypes[j], colStyles);
                }
            }

            return book;
        }

        /// <summary>
        ///  将一个实体数据对象填充到一个EXCEL工作表中(可连续填充多个sheet,如:FillSheet(...).FillSheet(..) )
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="book"></param>
        /// <param name="sheetName"></param>
        /// <param name="colMaps"></param>
        /// <param name="excelData"></param>
        /// <returns></returns>
        public static IWorkbook FillSheet<T>(this IWorkbook book, string sheetName, IList<T> excelData,
                IDictionary<string, Expression<Func<T, dynamic>>> colMaps, IDictionary<string, string> colDataFormats = null
            ) where T : class
        {

            var sheet = book.CreateSheet(sheetName);

            var headerColNames = new List<string>();
            var propInfos = new List<PropertyInfo>();

            foreach (var item in colMaps)
            {
                headerColNames.Add(item.Key);
                var propInfo = GetPropertyInfo(item.Value);
                propInfos.Add(propInfo);
            }

            var headerCellStyle = GetCellStyle(book, true);
            Dictionary<int, ICellStyle> colStyles = new Dictionary<int, ICellStyle>();
            IRow rowHeader = sheet.CreateRow(0);
            for (int i = 0; i < headerColNames.Count; i++)
            {
                ICell headerCell = rowHeader.CreateCell(i);
                headerCell.CellStyle = headerCellStyle;
                headerCell.SetCellValue(headerColNames[i]);

                if (colDataFormats != null && colDataFormats.ContainsKey(headerColNames[i]))
                {
                    colStyles[i] = GetCellStyleWithDataFormat(book, colDataFormats[headerColNames[i]]);
                }
                else
                {
                    colStyles[i] = GetCellStyle(book);
                }
            }

            //生成excel内容
            for (int i = 0; i < excelData.Count; i++)
            {
                IRow irow = sheet.CreateRow(i + 1);
                var row = excelData[i];
                for (int j = 0; j < headerColNames.Count; j++)
                {
                    var prop = propInfos[j];

                    ICell cell = irow.CreateCell(j);
                    string cellValue = Convert.ToString(propInfos[j].GetValue(row, null));
                    SetCellValue(cell, cellValue, prop.PropertyType, colStyles);
                }
            }

            return book;
        }

        /// <summary>
        /// 将一个数据表(DataTable)对象填充到一个EXCEL工作表中(可连续填充多个sheet,如:FillSheet(...).FillSheet(..) )
        /// </summary>
        /// <param name="book"></param>
        /// <param name="sheetName"></param>
        /// <param name="excelData"></param>
        /// <param name="colMaps"></param>
        /// <param name="colDataFormats"></param>
        /// <returns></returns>
        public static IWorkbook FillSheet(this IWorkbook book, string sheetName, DataTable excelData, IDictionary<string, string> colMaps,
            IDictionary<string, string> colDataFormats = null)
        {

            if (excelData.Rows.Count <= 0) return book;

            var sheet = book.CreateSheet(sheetName);

            var headerCellStyle = GetCellStyle(book, true);
            Dictionary<int, ICellStyle> colStyles = new Dictionary<int, ICellStyle>();
            IRow rowHeader = sheet.CreateRow(0);

            int nIndex = 0;
            var headerColNames = new List<string>();

            foreach (var item in colMaps)
            {
                ICell headerCell = rowHeader.CreateCell(nIndex);
                headerCell.SetCellValue(item.Value);
                headerCell.CellStyle = headerCellStyle;

                if (colDataFormats != null && colDataFormats.ContainsKey(item.Value))
                {
                    colStyles[nIndex] = GetCellStyleWithDataFormat(book, colDataFormats[item.Value]);
                }
                else
                {
                    colStyles[nIndex] = GetCellStyle(book);
                }
                headerColNames.Add(item.Key);
                nIndex++;
            }

            //生成excel内容
            for (int i = 0; i < excelData.Rows.Count; i++)
            {
                IRow irow = sheet.CreateRow(i + 1);
                var row = excelData.Rows[i];
                for (int j = 0; j < headerColNames.Count; j++)
                {
                    ICell cell = irow.CreateCell(j);
                    string colName = headerColNames[j];
                    string cellValue = row[colName].ToNotNullString();
                    SetCellValue(cell, cellValue, excelData.Columns[colName].DataType, colStyles);
                }
            }

            return book;
        }

        private static PropertyInfo GetPropertyInfo<T, TR>(Expression<Func<T, TR>> select)
        {
            var body = select.Body;
            if (body.NodeType == ExpressionType.Convert)
            {
                var o = (body as UnaryExpression).Operand;
                return (o as MemberExpression).Member as PropertyInfo;
            }
            else if (body.NodeType == ExpressionType.MemberAccess)
            {
                return (body as MemberExpression).Member as PropertyInfo;
            }
            return null;
        }

        private static Type GetColType(string colTypeSimpleName)
        {
            colTypeSimpleName = colTypeSimpleName.ToUpper();
            switch (colTypeSimpleName)
            {
                case "DT":
                    {
                        return typeof(DateTime);
                    }
                case "BL":
                    {
                        return typeof(Boolean);
                    }
                case "NUM":
                    {
                        return typeof(Int64);
                    }
                case "DEC":
                    {
                        return typeof(Decimal);
                    }
                default:
                    {
                        return typeof(String);
                    }
            }
        }

        private static void SetCellValue(ICell cell, string value, Type colType, IDictionary<int, ICellStyle> colStyles)
        {
            if (colType.IsNullableType())
            {
                colType = colType.GetGenericArguments()[0];
            }

            string dataFormatStr = null;
            switch (colType.ToString())
            {
                case "System.String": //字符串类型
                    cell.SetCellType(CellType.String);
                    cell.SetCellValue(value);
                    break;
                case "System.DateTime": //日期类型
                    DateTime dateV = new DateTime();
                    if (DateTime.TryParse(value, out dateV))
                    {
                        cell.SetCellValue(dateV);
                    }
                    else
                    {
                        cell.SetCellValue(value);
                    }
                    dataFormatStr = "yyyy/mm/dd hh:mm:ss";
                    break;
                case "System.Boolean": //布尔型
                    bool boolV = false;
                    if (bool.TryParse(value, out boolV))
                    {
                        cell.SetCellType(CellType.Boolean);
                        cell.SetCellValue(boolV);
                    }
                    else
                    {
                        cell.SetCellValue(value);
                    }
                    break;
                case "System.Int16": //整型
                case "System.Int32":
                case "System.Int64":
                case "System.Byte":
                    int intV = 0;
                    if (int.TryParse(value, out intV))
                    {
                        cell.SetCellType(CellType.Numeric);
                        cell.SetCellValue(intV);
                    }
                    else
                    {
                        cell.SetCellValue(value);
                    }
                    dataFormatStr = "0";
                    break;
                case "System.Decimal": //浮点型
                case "System.Double":
                    double doubV = 0;
                    if (double.TryParse(value, out doubV))
                    {
                        cell.SetCellType(CellType.Numeric);
                        cell.SetCellValue(doubV);
                    }
                    else
                    {
                        cell.SetCellValue(value);
                    }
                    dataFormatStr = "0.00";
                    break;
                case "System.DBNull": //空值处理
                    cell.SetCellType(CellType.Blank);
                    cell.SetCellValue(string.Empty);
                    break;
                default:
                    cell.SetCellType(CellType.Unknown);
                    cell.SetCellValue(value);
                    break;
            }

            if (!string.IsNullOrEmpty(dataFormatStr) && colStyles[cell.ColumnIndex].DataFormat <= 0) //没有设置,则采用默认类型格式
            {
                colStyles[cell.ColumnIndex] = GetCellStyleWithDataFormat(cell.Sheet.Workbook, dataFormatStr);
            }
            cell.CellStyle = colStyles[cell.ColumnIndex];

            ReSizeColumnWidth(cell.Sheet, cell);
        }

        private static ICellStyle GetCellStyleWithDataFormat(IWorkbook workbook, string format)
        {
            ICellStyle style = GetCellStyle(workbook);

            var dataFormat = workbook.CreateDataFormat();
            short formatId = -1;
            if (dataFormat is HSSFDataFormat)
            {
                formatId = HSSFDataFormat.GetBuiltinFormat(format);
            }
            if (formatId != -1)
            {
                style.DataFormat = formatId;
            }
            else
            {
                style.DataFormat = dataFormat.GetFormat(format);
            }
            return style;
        }

        private static ICellStyle GetCellStyle(IWorkbook workbook, bool isHeaderRow = false)
        {
            ICellStyle style = workbook.CreateCellStyle();

            if (isHeaderRow)
            {
                style.FillPattern = FillPattern.SolidForeground;
                style.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.Grey25Percent.Index;
                IFont f = workbook.CreateFont();
                f.FontHeightInPoints = 11D;
                f.Boldweight = (short)FontBoldWeight.Bold;
                style.SetFont(f);
            }

            style.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
            style.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
            style.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
            style.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
            return style;
        }

        private static void ReSizeColumnWidth(ISheet sheet, ICell cell)
        {
            int cellLength = (Encoding.Default.GetBytes(cell.ToString()).Length + 2) * 256;
            const int maxLength = 60 * 256; //255 * 256;
            if (cellLength > maxLength) //当单元格内容超过30个中文字符(英语60个字符)宽度,则强制换行
            {
                cellLength = maxLength;
                cell.CellStyle.WrapText = true;
            }
            int colWidth = sheet.GetColumnWidth(cell.ColumnIndex);
            if (colWidth < cellLength)
            {
                sheet.SetColumnWidth(cell.ColumnIndex, cellLength);
            }
        }

        private static ISheet GetSheet(IWorkbook workbook, string sheetIndexOrName)
        {
            int sheetIndex = 0;
            ISheet sheet = null;

            if (int.TryParse(sheetIndexOrName, out sheetIndex))
            {
                sheet = workbook.GetSheetAt(sheetIndex);
            }
            else
            {
                sheet = workbook.GetSheet(sheetIndexOrName);
            }
            return sheet;
        }

        /// <summary>
        /// 从工作表中解析生成DataTable
        /// </summary>
        /// <param name="workbook"></param>
        /// <param name="sheetIndexOrName"></param>
        /// <param name="headerRowIndex"></param>
        /// <param name="startColIndex"></param>
        /// <param name="colCount"></param>
        /// <returns></returns>
        public static DataTable ResolveDataTable(this IWorkbook workbook, string sheetIndexOrName, int headerRowIndex, short startColIndex = 0, short colCount = 0)
        {
            DataTable table = new DataTable();

            ISheet sheet = GetSheet(workbook, sheetIndexOrName);

            IRow headerRow = sheet.GetRow(headerRowIndex);
            int cellFirstNum = (startColIndex > headerRow.FirstCellNum ? startColIndex : headerRow.FirstCellNum);
            int cellCount = (colCount > 0 && colCount < headerRow.LastCellNum ? colCount : headerRow.LastCellNum);

            for (int i = cellFirstNum; i < cellCount; i++)
            {
                if (headerRow.GetCell(i) == null || headerRow.GetCell(i).StringCellValue.Trim() == "")
                {
                    // 如果遇到第一个空列,则不再继续向后读取
                    cellCount = i;
                    break;
                }
                DataColumn column = new DataColumn(headerRow.GetCell(i).StringCellValue);
                table.Columns.Add(column);
            }

            for (int i = (headerRowIndex + 1); i <= sheet.LastRowNum; i++)
            {
                IRow row = sheet.GetRow(i);
                if (row != null)
                {
                    List<string> cellValues = new List<string>();
                    for (int j = cellFirstNum; j < cellCount; j++)
                    {
                        if (row.GetCell(j) != null)
                        {
                            cellValues.Add(row.GetCell(j).ToNotNullString());
                        }
                        else
                        {
                            cellValues.Add(string.Empty);
                        }
                    }

                    table.Rows.Add(cellValues.ToArray());
                }
            }

            return table;
        }

        /// <summary>
        /// 从工作表中解析生成指定的结果对象列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="workbook"></param>
        /// <param name="sheetIndexOrName"></param>
        /// <param name="headerRowIndex"></param>
        /// <param name="buildResultItemFunc"></param>
        /// <param name="startColIndex"></param>
        /// <param name="colCount"></param>
        /// <returns></returns>
        public static List<T> ResolveAs<T>(this IWorkbook workbook, string sheetIndexOrName, int headerRowIndex, Func<List<string>, T> buildResultItemFunc,
            short startColIndex = 0, short colCount = 0)
        {
            ISheet sheet = GetSheet(workbook, sheetIndexOrName);

            IRow headerRow = sheet.GetRow(headerRowIndex);
            int cellFirstNum = (startColIndex > headerRow.FirstCellNum ? startColIndex : headerRow.FirstCellNum);
            int cellCount = (colCount > 0 && colCount < headerRow.LastCellNum ? colCount : headerRow.LastCellNum);

            List<T> resultList = new List<T>();
            for (int i = (headerRowIndex + 1); i <= sheet.LastRowNum; i++)
            {
                IRow row = sheet.GetRow(i);
                if (row != null)
                {
                    List<string> cellValues = new List<string>();
                    for (int j = cellFirstNum; j < cellCount; j++)
                    {
                        if (row.GetCell(j) != null)
                        {
                            cellValues.Add(row.GetCell(j).ToNotNullString());
                        }
                        else
                        {
                            cellValues.Add(string.Empty);
                        }
                    }

                    resultList.Add(buildResultItemFunc(cellValues));
                }
            }

            return resultList;
        }

        public static MemoryStream ToExcelStream(this IWorkbook book)
        {
            if (book.NumberOfSheets <= 0)
            {
                throw new Exception("无有效的sheet数据");
            }

            MemoryStream stream = new MemoryStream();

            stream.Seek(0, SeekOrigin.Begin);
            book.Write(stream);

            return stream;
        }

        public static byte[] ToExcelBytes(this IWorkbook book)
        {
            using (MemoryStream stream = ToExcelStream(book))
            {
                return stream.ToArray();
            }
        }

        public static JObject HttpUpload(this IWorkbook book, string uploadUrl, IDictionary<string, object> fieldData = null, string exportFileName = null)
        {
            using (HttpClient client = new HttpClient())
            {
                MultipartFormDataContent formData = new MultipartFormDataContent();
                ByteArrayContent fileContent = new ByteArrayContent(ToExcelBytes(book));
                //StreamContent fileContent = new StreamContent(ToExcelStream(book)); 二者都可以
                fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");

                if (string.IsNullOrWhiteSpace(exportFileName))
                {
                    exportFileName = Guid.NewGuid().ToString("N") + ((book is XSSFWorkbook) ? ".xlsx" : ".xls");
                }

                fileContent.Headers.ContentDisposition.FileName = exportFileName;
                fileContent.Headers.ContentDisposition.Name = "file";
                formData.Add(fileContent);

                Func<string, StringContent> getStringContent = (str) => new StringContent(str, Encoding.UTF8);

                if (fieldData != null)
                {
                    foreach (var header in fieldData)
                    {
                        formData.Add(getStringContent(header.Value.ToNotNullString()), header.Key);
                    }
                }

                HttpResponseMessage res = client.PostAsync(uploadUrl, formData).Result;
                string resContent = res.Content.ReadAsStringAsync().Result;
                return JObject.Parse(resContent);
            }
        }

        public static string SaveToFile(this IWorkbook book, string filePath)
        {
            if (File.Exists(filePath))
            {
                File.SetAttributes(filePath, FileAttributes.Normal);
                File.Delete(filePath);
            }

            using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                book.Write(fs);
            }

            return filePath;
        }

    }

}

 如上代码都比较简单没有什么复杂要,故不再细述。还有一个Core类,这个只是一个入口帮助类,如果已有获得IWorkbook的自定义方法,该类可以不用,代码如下:

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Text;

namespace ExcelEasyUtil
{
    /// <summary>
    /// NPOI 相关核心入口方法帮助类
    /// author:zuowenjun
    /// 2019-5-21
    /// </summary>
    public static class Core
    {
        /// <summary>
        /// 创建一个基本XLSX格式的EXCEL工作薄对象
        /// </summary>
        /// <returns></returns>
        public static IWorkbook CreateXlsxWorkBook()
        {
            return new XSSFWorkbook();
        }

        /// <summary>
        /// 创建一个基本XLS格式的EXCEL工作薄对象
        /// </summary>
        /// <returns></returns>
        public static IWorkbook CreateXlsWorkBook()
        {
            return new HSSFWorkbook();
        }

        /// <summary>
        /// 打开指定文件的EXCEL工作薄对象
        /// </summary>
        /// <param name="filePath">Excel文件路径</param>
        /// <returns></returns>
        public static IWorkbook OpenWorkbook(string filePath)
        {
            bool isCompatible = filePath.EndsWith(".xls", StringComparison.OrdinalIgnoreCase);
            var fileStream = System.IO.File.OpenRead(filePath);
            if (isCompatible)
            {
                return new HSSFWorkbook(fileStream);
            }
            else
            {
                return new XSSFWorkbook(fileStream);
            }
        }

    }
}

 虽然代码简单,但方法命名稍有些讲究,易阅读与理解,CreateXlsxWorkBook就是表现创建一个新的基于xlsx格式的工作薄对象,而CreateXlsWorkBook就是表现创建一个新的基于xls格式的工作薄对象,我并没合成一个方法,然后使用一个bool参数或enum参数来区分,我觉得在这个追求极简、高效的开发理念中,方法名要易于理解与快速上手,不应产生歧义,也无需太多的参数。OpenWorkbook就是打开一个已有的EXCEL文件工作薄对象。

代码虽简单,但我通过合理的封装入参,委托参数等,实现了简单但又不失灵活的EXCEL操作方式,没有一味追求极简,把所有的复杂操作全部封装,从而导致本来简单的事情搞得复杂化,比如:将一个List<T>导出到EXCEL中,常见的实现方式肯定就是通过反射获得属性信息,再利用属性信息获得值,这是做虽入参简单,但性能不好,而本文的这个类库将取值或赋值的方式通过Func委托交由给具体使用的地方,从而避免了反射带来的性能损失,好了,如下展示几个使用示例,帮助大家理解和使用。

第一种填充sheet方式:(重点在表格头的映射设置,通过Lamba属性表达式与要生成的EXCEL表头进行关联映射)

var book= ExcelEasyUtil.Core.CreateXlsxWorkBook()
                .FillSheet("人员列表1", peoples,//填充第一个表格
                //new Dictionary<string, Expression<Func<People, dynamic>>> //设置表格头,原始类型
               new PropertyColumnMapping<People> //设置表格头,专用简化类型
               {
                {"姓名",p=>p.Name },{"年龄",p=>p.Age },{"生日",p=>p.Birthday },{"住址",p=>p.Address },{"学历",p=>p.Education },
                { "有工作否",p=>p.hasWork },{"备注",p=>p.Remark }
               },
               new Dictionary<string, string> //为指定的列设置单元格格式
               {
                { "年龄","0岁"},{"生日","yyyy年mm月dd日"}
               });

第二种填充sheet方式:(重点在表格头类型设置【:XXX表示生成的EXCEL该列为某种格式的单元格,如:生日:DT表示是生日这列导出是日期类型格式】,第二个参数返回List<object>这个是可以很好的控制导出时需要的填充数据,可以自由控制数据,比如示例代码中额外增加了一列判断的数据列内容,第三个参数是为指定的列设置单元格的具体应用格式)

var book= ExcelEasyUtil.Core.CreateXlsxWorkBook()
.FillSheet("人员列表2", peoples,  //填充第二个表格
               new List<string>
               {
                   "姓名","年龄:NUM","生日:DT","住址","学历","有工作否:BL","备注","额外填充列"
               }, (p) =>
               {
                   return new List<object> {
                       p.Name,p.Age,p.Birthday,p.Address,p.Education,p.hasWork?"有":"无",p.Remark,(p.Age<=30 && p.hasWork)?"年轻有为":"要么老了要么没工作,生活堪忧"
                   };
               }, new Dictionary<string, string>
               {
                   { "生日","yyyy-mm-dd"}
               });

 第三种填充sheet方式:(重点仍然是在表头映射,由于这里的数据源是DataTable,故只是设置DataTable的列与EXCEL要导出的列名映射即可,无需指定列类型,第二个参数是为指定的列设置单元格的具体应用格式)

var book= ExcelEasyUtil.Core.CreateXlsxWorkBook()
 .FillSheet("人员列表3", peoplesTable, //填充第三个表格
               new Dictionary<string, string> {
                   {"Name","姓名" },{"Birthday","生日" },{"Address","住址" },{"Education","学历" }, {"hasWork","有工作否" },{"Remark","备注" }
               }
               , new Dictionary<string, string>
               {
                   { "生日","yyyy-mm-dd"}
               });

 由于实现了FillSheet方法后仍返回IWorkBook实例本身,即可采用链式的方式来快速完成多个sheet导出的,合并代码如下:

 string savedPath = ExcelEasyUtil.Core.CreateXlsxWorkBook()
                .FillSheet("人员列表1", peoples,//填充第一个表格
               //new Dictionary<string, Expression<Func<People, dynamic>>> //设置表格头,原始类型
               new PropertyColumnMapping<People> //设置表格头,专用简化类型
               {
                {"姓名",p=>p.Name },{"年龄",p=>p.Age },{"生日",p=>p.Birthday },{"住址",p=>p.Address },{"学历",p=>p.Education },
                { "有工作否",p=>p.hasWork },{"备注",p=>p.Remark }
               },
               new Dictionary<string, string> //为指定的列设置单元格格式
               {
                { "年龄","0岁"},{"生日","yyyy年mm月dd日"}
               })
               .FillSheet("人员列表2", peoples,  //填充第二个表格
               new List<string>
               {
                   "姓名","年龄:NUM","生日:DT","住址","学历","有工作否:BL","备注","额外填充列"
               }, (p) =>
               {
                   return new List<object> {
                       p.Name,p.Age,p.Birthday,p.Address,p.Education,p.hasWork?"有":"无",p.Remark,(p.Age<=30 && p.hasWork)?"年轻有为":"要么老了要么没工作,生活堪忧"
                   };
               }, new Dictionary<string, string>
               {
                   { "生日","yyyy-mm-dd"}
               })
               .FillSheet("人员列表3", peoplesTable, //填充第三个表格
               new Dictionary<string, string> {
                   {"Name","姓名" },{"Birthday","生日" },{"Address","住址" },{"Education","学历" }, {"hasWork","有工作否" },{"Remark","备注" }
               }
               , new Dictionary<string, string>
               {
                   { "生日","yyyy-mm-dd"}
               })
               .SaveToFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "testdata123.xlsx"));

            Console.WriteLine("导出EXCEL文件路径:" + savedPath);

 感觉如何,EXCEL导出既能设置列头,列格式,还能控制填充的数据值,认为是否够方便呢,我个人觉得还是可以满足日常大部份的使用EXCEL的需求。

 说了导出EXCEL方法再来贴出导入EXCEL数据(这里称为解析EXCEL数据)的示例用法:

 var xlsTable = ExcelEasyUtil.Core.OpenWorkbook(savedPath).ResolveDataTable("人员列表1", 0);
            foreach (DataRow row in xlsTable.Rows)
            {
                string rowStr = string.Join("\t", row.ItemArray);
                Console.WriteLine(rowStr);
            }

            var xlsPeoples = ExcelEasyUtil.Core.OpenWorkbook(savedPath).ResolveAs<People>("人员列表2", 0, list =>
            {
                return new People
                {
                    Name = list[0],
                    Birthday = ConvertToDate(list[2]),//日期处理相对较麻烦
                    Address = list[3]
                };
            }, 0, 4);

            Console.WriteLine("-".PadRight(30,‘-‘));
            foreach (var p in xlsPeoples)
            {
                string rowStr = string.Join("\t", p.Name, p.Age, p.Birthday, p.Address);
                Console.WriteLine(rowStr);
            }

这里仅实现了解析到DataTable或解析到所需的对象集合,一般也能满足常见的导入数据的要求。

当然还有:HttpUpload方法,这个主要是把生成的内存EXCEL文件直接上传至指定的文件服务器的,如果是在ASP.NET,ASP.NET CORE服务端需要下载,那么则可以采用先使用:book.ToExcelBytes(),然后构造文件字节流Result即可,比如:MVC中的File,可以参照我本文一开始列举的第二中方法中的导出,在此就不再贴示例了。

好了,以上就是本文的全部内容,可能大神们认为这太简单了,但我认为简单高效的实用类库还是需要的,因为ExcelEasyUtil本身就是为简单、轻量、实用而封装的,故不会有太多复杂的东西,如果需要复杂的EXCEL操作就使用我上面说的第四种方案:ExcelUtility。

若有不足之处或好的建议,欢迎评论,谢谢!

本文ExcelEasyUtil源代码地址:https://github.com/zuowj/ExcelEasyUtil   (文中的所有示例代码在github均有)

为了方便开发者使用,还封装成了NuGet包: 

Packge Manager:Install-Package ExcelEasyUtil -Version 1.0.0

.NET CLI:dotnet add package ExcelEasyUtil --version 1.0.0 OR <PackageReference Include="ExcelEasyUtil" Version="1.0.0" />

原文地址:https://www.cnblogs.com/zuowj/p/10987224.html

时间: 2024-10-11 18:24:25

【EXCEL终极总结分享】基于NPOI扩展封装的简易操作工具类库(简单灵活易用,支持导出、导入、上传等常见操作)的相关文章

基于bootstrap的上传插件fileinput实现ajax异步上传功能(支持多文件上传预览拖拽)

首先需要导入一些js和css文件 ? 1 2 3 4 5 6 <link href="__PUBLIC__/CSS/bootstrap.css" rel="external nofollow" rel="stylesheet"> <link type="text/css" rel="stylesheet" href="__PUBLIC__/CSS/fileinput.css&qu

封装了okhttp的网络框架,支持大文件上传下载,上传进度...

本帖最后由 anjoy紫外线 于 2016-4-20 16:42 编辑   1.用法 对于Eclipse不能运行项目的,提供了apk供直接运行,位于项目根目录 okhttputils_v1.x.x.apk. 本项目Demo的网络请求是我自己的服务器,有时候可能不稳定,网速比较慢时请耐心等待.. 对于Android Studio的用户,可以选择添加: compile 'com.lzy.net:okhttputils:1.3.0'  //可以单独使用,不需要依赖下方的扩展包    compile'c

CMDB项目CURD组件之基于jQuery扩展封装组件

request.body from django.shortcuts import render,HttpResponse from django.views import View import json class AssetView(View): def get(self,request,*args,**kwargs): # 数据库中获取数据 return render(request,'asset.html') class AssetJsonView(View): def get(sel

基于 java 注解的 csv 读写框架更加简单灵活

CSV 基于 java 注解的 csv 读写框架. 相关框架 Apache commons-csv super-csv 简单看了下,这两个框架提供的特性都非常的基础. 创作原由 以前觉得 csv 文件的读写非常简单,就懒得封装. 最近一个月写了两次 csv 文件相关的东西,发现要处理的细节还是有的,还浪费比较多的时间. 比如: UTF-8 中文编码使用 excel 打开乱码,因为缺少 BOM 头. 不同类型字段转化为字符串,顺序的指定,head 头的指定,如果手写都会很繁琐. 读取的时候最后 ,

支持多文件上传,预览,拖拽,基于bootstra的上传插件fileinput 的ajax异步上传

首先需要导入一些js和css文件 <link href="__PUBLIC__/CSS/bootstrap.css" rel="stylesheet"> <link type="text/css" rel="stylesheet" href="__PUBLIC__/CSS/fileinput.css" /> <script type="text/javascript&

NPOI操作excel——利用反射机制,NPOI读取excel数据准确映射到数据库字段

> 其实需求很明确,就是一大堆不一样的excel,每张excel对应数据库的一张表,我们需要提供用户上传excel,我们解析数据入库的功能实现. 那么,这就涉及到一个问题:我们可以读出excel的表头,但是怎么知道每个表头具体对应数据库里面的字段呢? 博主经过一段时间的思考与构思,想到一法:现在的情况是我们有excel表A,对应数据库表B,但是A与B具体属性字段的映射关系我们不知.那我们是不是可以有一个A到B的映射文件C呢? 我想,说到这,大家就很明了了... 第一步:为每张excel创建一个与

结合bootstrap fileinput插件和Bootstrap-table表格插件,实现文件上传、预览、提交的导入Excel数据操作流程

1.bootstrap-fileinpu的简单介绍 在前面的随笔,我介绍了Bootstrap-table表格插件的具体项目应用过程,本篇随笔介绍另外一个Bootstrap FieInput插件的使用,整合两者可以实现我们常规的Web数据导入操作,导入数据操作过程包括有上传文件,预览数据,选择并提交记录等一系列操作. 关于这个插件,我在早期随笔<Bootstrap文件上传插件File Input的使用>也做了一次介绍,这是一个增强的 HTML5 文件输入控件,是一个 Bootstrap 3.x

基于MVC4+EasyUI的Web开发框架形成之旅--附件上传组件uploadify的使用

很久之前,当我还在用Asp.NET开发一些行业管理系统的时候,就曾经使用这个组件作为文件的上传操作,在随笔<Web开发中的文件上传组件uploadify的使用>中可以看到,Asp.NET中如何使用这个组件进行文件上传的,当时上传文件的处理主要也是使用ashx一般处理程序来进行处理的.本文主要介绍我的Web开发框架中,在MVC4的环境中如何集成这个非常棒的文件上传组件的. 1.上传组件uploadify的说明及脚本引用 Uploadify 是 JQuery 一个著名的上传插件,利用 Flash

基于MVC4+EasyUI的Web开发框架形成之旅(4)--附件上传组件uploadify的使用

大概一年前,我还在用Asp.NET开发一些行业管理系统的时候,就曾经使用这个组件作为文件的上传操作,在随笔<Web开发中的文件上传组件uploadify的使用>中可以看到,Asp.NET中如何使用这个组件进行文件上传的,当时上传文件的处理主要也是使用ashx一般处理程序来进行处理的.本文主要介绍我的Web开发框架中,在MVC4的环境中如何集成这个非常棒的文件上传组件的. 1.上传组件uploadify的说明及脚本引用 Uploadify 是 JQuery 一个著名的上传插件,利用 Flash