要在ASP.NET MVC站点上做excel导出功能,但是要导出的excel文件比较大,有几十M,所以导出比较费时,为了不影响对界面的其它操作,我就采用异步的方式,后台开辟一个线程将excel导出到指定目录,然后提供下载。导出的excel涉及到了多个sheet(工作簿),表格合并,格式设置等,所以采用了NPOI组件。

效果如下:

ASP.NET MVC导出excel(数据量大,非常耗时的,异步导出)ASP.NET MVC导出excel(数据量大,非常耗时的,异步导出)ASP.NET MVC导出excel(数据量大,非常耗时的,异步导出)

选中了多行,会导出多个工作簿sheet,一个汇总的,其他的就是明细数据。

ASP.NET MVC导出excel(数据量大,非常耗时的,异步导出)

ASP.NET MVC导出excel(数据量大,非常耗时的,异步导出)ASP.NET MVC导出excel(数据量大,非常耗时的,异步导出)

下面是要几个封装好的类,从网上找的,然后修改了一下。这几个类很多方法都封装好了,十分利于复用。常见的excel格式都可以导出,如果有特别的需求,可以自己修改一下源码进行扩展。

GenerateSheet.cs

using NPOI.SS.UserModel;
using NPOI.SS.Util;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Core.Excel
{
    /// <summary>
    /// 导出Excel基类
    /// </summary>
    public class GenerateSheet<T> : BaseGenerateSheet
    {
        #region 私有字段
        // Excel 显示时间的样式
        private ICellStyle dateStyle = null;
        // Excel 显示列头的样式
        private ICellStyle headStyle = null;
        // Excel 显示内容的样式
        private ICellStyle contentsStyle = null;
        // Excel 显示总计的样式
        private ICellStyle totalStyle = null;
        // 列头集合
        private List<ColumnsMapping> columnHeadList = null;
        // 显示的数据
        private List<T> dataSource;

        private List<object> dataSource2;
        #endregion

        #region 属性
        /// <summary>
        /// Excel 显示时间的样式
        /// </summary>
        protected ICellStyle DateStyle
        {
            get { return dateStyle; }
            set { dateStyle = value; }
        }
        /// <summary>
        /// Excel 显示列头的样式
        /// </summary>
        protected ICellStyle HeadStyle
        {
            get { return headStyle; }
            set { headStyle = value; }
        }
        /// <summary>
        /// Excel 显示内容的样式
        /// </summary>
        protected ICellStyle ContentsStyle
        {
            get { return contentsStyle; }
            set { contentsStyle = value; }
        }
        /// <summary>
        /// Excel 显示总计的样式
        /// </summary>
        protected ICellStyle TotalStyle
        {
            get { return totalStyle; }
            set { totalStyle = value; }
        }
        /// <summary>
        /// 是否有边框 只读
        /// </summary>
        protected bool IsBorder { get; private set; }

        protected List<ColumnsMapping> ColumnHeadList
        {
            get { return this.columnHeadList; }
            private set { this.columnHeadList = value; }
        }
        #endregion

        #region 构造方法
        /// <summary>
        /// 导出Excel基类
        /// </summary>
        /// <param name="_dataSource">Sheet里面显示的数据</param>
        public GenerateSheet(List<T> _dataSource)
            : this(_dataSource, null, string.Empty, true)
        {
        }
        /// <summary>
        /// 导出Excel基类
        /// </summary>
        /// <param name="_dataSource">Sheet里面显示的数据</param>
        public GenerateSheet(List<T> _dataSource, string sheetName)
            : this(_dataSource, null, sheetName, true)
        {
        }

        /// <summary>
        /// 导出Excel基类
        /// </summary>
        /// <param name="_dataSource">Sheet里面显示的数据</param>
        public GenerateSheet(List<T> _dataSource, List<object> _dataSource2, string sheetName)
            : this(_dataSource, _dataSource2, sheetName, true)
        {
        }

        /// <summary>
        /// 导出Excel基类
        /// </summary>
        /// <param name="_dataSource">Sheet里面显示的数据</param>
        /// <param name="isBorder">是否有边框</param>
        public GenerateSheet(List<T> _dataSource, bool isBorder)
            : this(_dataSource, null, string.Empty, isBorder)
        {
        }
        /// <summary>
        /// 导出Excel基类
        /// </summary>
        /// <param name="_dataSource">Sheet里面显示的数据</param>
        /// <param name="isBorder">是否有边框</param>
        public GenerateSheet(List<T> _dataSource, List<object> _dataSource2, string sheetName, bool isBorder)
        {
            //if (_dataSource != null && _dataSource.Count > 0)
            this.dataSource = _dataSource;
            this.dataSource2 = _dataSource2;
            //else
            //    throw new Exception("数据不能为空!");
            this.IsBorder = isBorder;
            this.SheetName = sheetName;
        }
        #endregion

        #region 可以被重写的方法
        /// <summary>
        /// 生成Excel的Sheet
        /// </summary>
        /// <param name="sheet"></param>
        public override void GenSheet(ISheet sheet)
        {
            this.SetSheetContents(sheet);
        }
        /// <summary>
        /// 初始化列头
        /// </summary>
        /// <returns></returns>
        protected virtual List<ColumnsMapping> InitializeColumnHeadData()
        {
            try
            {
                List<PropertyInfo> propertyList = this.GetObjectPropertyList();
                List<ColumnsMapping> columnsList = new List<ColumnsMapping>();
                int colIndex = 0;
                foreach (PropertyInfo propertyInfo in propertyList)
                {
                    columnsList.Add(new ColumnsMapping()
                    {
                        ColumnsData = propertyInfo.Name,
                        ColumnsText = propertyInfo.Name,
                        ColumnsIndex = colIndex,
                        IsTotal = false,
                        Width = 15
                    });
                    colIndex++;
                }
                return columnsList;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 设置列头
        /// </summary>
        /// <param name="sheet">Excel Sheet</param>
        /// <param name="rowIndex">记录Excel最大行的位置,最大值为65535</param>
        protected virtual void SetColumnHead(ISheet sheet, ref int rowIndex)
        {
            if (columnHeadList.Count > 0)
            {
                IRow headerRow = sheet.CreateRow(rowIndex);
                foreach (ColumnsMapping columns in columnHeadList)
                {
                    ICell newCell = headerRow.CreateCell(columns.ColumnsIndex);
                    newCell.SetCellValue(columns.ColumnsText);
                    newCell.CellStyle = headStyle;
                    //设置列宽
                    SetColumnsWidth(sheet, columns.ColumnsIndex, columns.Width);
                }
                rowIndex++;
            }
        }
        /// <summary>
        /// 设置Excel内容
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="dataSource"></param>
        /// <param name="rowIndex"></param>
        protected virtual void SetSheetContents(ISheet sheet, List<T> dataSource, ref int rowIndex)
        {
            if (dataSource != null)
            {
                foreach (T value in dataSource)
                {
                    #region 填充内容
                    IRow dataRow = sheet.CreateRow(rowIndex);
                    int colIndex = 0;
                    foreach (ColumnsMapping columns in columnHeadList)
                    {
                        if (columns.ColumnsIndex >= 0)
                        {
                            if (columns.ColumnsIndex >= colIndex)
                                colIndex = columns.ColumnsIndex;
                            else
                                columns.ColumnsIndex = colIndex;

                            ICell newCell = dataRow.CreateCell(colIndex);
                            string drValue = string.Empty;
                            if (!string.IsNullOrEmpty(columns.ColumnsData))
                                drValue = GetModelValue(columns.ColumnsData, value);
                            SetCellValue(newCell, rowIndex, drValue, columns);
                            colIndex++;
                        }
                    }
                    #endregion

                    rowIndex++;
                }
                //rowIndex++;
            }
        }
        /// <summary>
        /// 设置单元格的数据
        /// </summary>
        /// <param name="cell">单元格对像</param>
        /// <param name="rowIndex">单元格行索引</param>
        /// <param name="drValue">单元格数据</param>
        /// <param name="columns">单元格的列信息</param>
        protected virtual void SetCellValue(ICell cell, int rowIndex, string drValue, ColumnsMapping columns)
        {
            cell.CellStyle = contentsStyle;
            if (!string.IsNullOrEmpty(columns.ColumnsData))
            {
                PropertyInfo info = GetObjectProperty(columns.ColumnsData);
                switch (info.PropertyType.FullName)
                {
                    case "System.String": //字符串类型
                        double result;
                        if (IsNumeric(drValue, out result))
                        {
                            double.TryParse(drValue, out result);
                            cell.SetCellValue(result);
                            break;
                        }
                        else
                        {
                            cell.SetCellValue(drValue);
                            break;
                        }
                    case "System.DateTime": //日期类型
                        if (string.IsNullOrEmpty(drValue)||drValue=="0001/1/1 0:00:00")
                        {
                            cell.SetCellValue("");
                        }
                        else
                        {
                            DateTime dateV;
                            DateTime.TryParse(drValue, out dateV);
                            cell.SetCellValue(dateV);
                            cell.CellStyle = dateStyle; //格式化显示
                        }
                        break;
                    case "System.Boolean": //布尔型
                        bool boolV = false;
                        bool.TryParse(drValue, out boolV);
                        cell.SetCellValue(boolV);
                        break;
                    case "System.Int16": //整型
                    case "System.Int32":
                    case "System.Int64":
                    case "System.Byte":
                        int intV = 0;
                        int.TryParse(drValue, out intV);
                        cell.SetCellValue(intV);
                        break;
                    case "System.Decimal": //浮点型
                    case "System.Double":
                        double doubV = 0;
                        double.TryParse(drValue, out doubV);
                        cell.SetCellValue(doubV);
                        break;
                    case "System.DBNull": //空值处理
                        cell.SetCellValue("");
                        break;
                    default:
                        cell.SetCellValue("");
                        break;
                }
            }
            else
            {
                cell.SetCellValue("");
            }
        }
        /// <summary>
        /// 设置总计单元格的数据
        /// </summary>
        /// <param name="cell">总计单元格</param>
        /// <param name="rowIndex">当前行的索引</param>
        /// <param name="startRowIndex">内容数据的开始行</param>
        /// <param name="columns">当前列信息</param>
        protected virtual void SetTotalCellValue(ICell cell, int rowIndex, int startRowIndex, ColumnsMapping columns)
        {
            if (columns.IsTotal)
            {
                string colItem = CellReference.ConvertNumToColString(columns.ColumnsIndex);
                cell.CellStyle = totalStyle;
                cell.SetCellFormula(string.Format("SUM({0}{1}:{2}{3})", colItem, startRowIndex, colItem, rowIndex));
            }
        }
        /// <summary>
        /// 在所有数据最后添加总计,当然也可以是其它的公式
        /// </summary>
        /// <param name="sheet">工作薄Sheet</param>
        /// <param name="rowIndex">当前行</param>
        /// <param name="startRowIndex">内容开始行</param>
        protected virtual void SetTotal(ISheet sheet, ref int rowIndex, int startRowIndex)
        {
            if (rowIndex > startRowIndex)
            {
                IRow headerRow = sheet.CreateRow(rowIndex) as IRow;
                foreach (ColumnsMapping columns in columnHeadList)
                {
                    ICell newCell = headerRow.CreateCell(columns.ColumnsIndex);
                    SetTotalCellValue(newCell, rowIndex, startRowIndex, columns);
                }
            }
        }
        
        /// <summary>
        /// 数据源2
        /// </summary>
        /// <param name="sheet">工作薄Sheet</param>
        /// <param name="rowIndex">当前行</param>
        protected virtual void SetToSecond(ISheet sheet, ref int rowIndex, List<object> dataSource2)
        {
            
        }
        #endregion

        #region 公共方法
        /// <summary>
        /// 获取属性名字
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        protected string GetPropertyName(Expression<Func<T, object>> expr)
        {
            var rtn = "";
            if (expr.Body is UnaryExpression)
            {
                rtn = ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member.Name;
            }
            else if (expr.Body is MemberExpression)
            {
                rtn = ((MemberExpression)expr.Body).Member.Name;
            }
            else if (expr.Body is ParameterExpression)
            {
                rtn = ((ParameterExpression)expr.Body).Type.Name;
            }
            return rtn;
        }

        protected void SetColumnsWidth(ISheet sheet, int colIndex, int width)
        {
            //设置列宽
            sheet.SetColumnWidth(colIndex, width * 256);
        }
        #endregion

        #region 私有方法
        private void SetSheetContents(ISheet sheet)
        {
            if (sheet != null)
            {
                // 初始化相关样式
                this.InitializeCellStyle();
                // 初始化列头的相关数据
                this.columnHeadList = InitializeColumnHeadData();
                // 当前行
                int rowIndex = 0;
                // 设置列头
                this.SetColumnHead(sheet, ref rowIndex);
                // 内容开始行
                int startRowIndex = rowIndex;
                // 设置Excel内容
                this.SetSheetContents(sheet, dataSource, ref rowIndex);
                // 在所有数据最后添加总计,当然也可以是其它的公式
                if (dataSource.Count > 0)
                {
                    this.SetTotal(sheet, ref rowIndex, startRowIndex);
                }

                this.SetToSecond(sheet, ref rowIndex, dataSource2);
            }
        }
        /// <summary>
        /// 初始化相关对像
        /// </summary>
        private void InitializeCellStyle()
        {
            columnHeadList = new List<ColumnsMapping>();

            // 初始化Excel 显示时间的样式
            dateStyle = this.Workbook.CreateCellStyle();
            IDataFormat format = this.Workbook.CreateDataFormat();
            dateStyle.DataFormat = format.GetFormat("yyyy-mm-dd");
            if (this.IsBorder)
            {
                //有边框
                dateStyle.BorderBottom = BorderStyle.Thin;
                dateStyle.BorderLeft = BorderStyle.Thin;
                dateStyle.BorderRight = BorderStyle.Thin;
                dateStyle.BorderTop = BorderStyle.Thin;
            }

            // 初始化Excel 列头的样式
            headStyle = this.Workbook.CreateCellStyle();
            headStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left;// 文本居左
            IFont font = this.Workbook.CreateFont();
            font.FontHeightInPoints = 12;   // 字体大小
            font.Boldweight = 700;          // 字体加粗
            headStyle.SetFont(font);

            if (this.IsBorder)
            {
                //有边框
                headStyle.BorderBottom = BorderStyle.Thin;
                headStyle.BorderLeft = BorderStyle.Thin;
                headStyle.BorderRight = BorderStyle.Thin;
                headStyle.BorderTop = BorderStyle.Thin;
            }

            // 初始化Excel 显示内容的样式
            contentsStyle = this.Workbook.CreateCellStyle();
            font = this.Workbook.CreateFont();
            font.FontHeightInPoints = 10;
            contentsStyle.SetFont(font);
            if (this.IsBorder)
            {
                //有边框
                contentsStyle.BorderBottom = BorderStyle.Thin;
                contentsStyle.BorderLeft = BorderStyle.Thin;
                contentsStyle.BorderRight = BorderStyle.Thin;
                contentsStyle.BorderTop = BorderStyle.Thin;
            }

            // 初始化Excel 显示总计的样式
            totalStyle = this.Workbook.CreateCellStyle();
            font = this.Workbook.CreateFont();
            font.Boldweight = 700;
            font.FontHeightInPoints = 10;
            totalStyle.SetFont(font);
            if (this.IsBorder)
            {
                //有边框
                totalStyle.BorderBottom = BorderStyle.Thin;
                totalStyle.BorderLeft = BorderStyle.Thin;
                totalStyle.BorderRight = BorderStyle.Thin;
                totalStyle.BorderTop = BorderStyle.Thin;
            }
        }
        /// <summary>
        /// 获取 T 对像的所有属性
        /// </summary>
        /// <returns></returns>
        private List<PropertyInfo> GetObjectPropertyList()
        {
            List<PropertyInfo> result = new List<PropertyInfo>();
            Type t = typeof(T);
            if (t != null)
            {
                PropertyInfo[] piList = t.GetProperties();
                foreach (var pi in piList)
                {
                    if (!pi.PropertyType.IsGenericType)
                    {
                        result.Add(pi);
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 根据属性名字获取 T 对像的属性
        /// </summary>
        /// <returns></returns>
        private PropertyInfo GetObjectProperty(string propertyName)
        {
            Type t = typeof(T);
            PropertyInfo result = t.GetProperty(propertyName);
            return result;
        }
        /// <summary>
        /// 获取类中的属性值
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        private string GetModelValue(string FieldName, object obj)
        {
            try
            {
                Type Ts = obj.GetType();
                object o = Ts.GetProperty(FieldName).GetValue(obj, null);
                string Value = Convert.ToString(o);
                if (string.IsNullOrEmpty(Value)) return null;
                return Value;
            }
            catch
            {
                return null;
            }
        }
        /// <summary>
        /// 判断是否为一个数字并反回值
        /// </summary>
        /// <param name="message"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        private bool IsNumeric(String message, out double result)
        {
            if (!string.IsNullOrEmpty(message))
            {
                Regex rex = new Regex(@"^[-]?\d+[.]?\d*$");
                result = -1;
                if (rex.IsMatch(message))
                {
                    result = double.Parse(message);
                    return true;
                }
                else
                    return false;
            }
            else
            {
                result = 0;
                return false;
            }
        }
        #endregion
    }
}
View Code

相关文章: