先上测试代码:

            string connectionString = "Server=localhost;Initial Catalog=******;User ID=sa;Password=******;";
            List<TestData> datas = null;
            using (SqlConnection db = new SqlConnection(connectionString))
            {
                datas = db.Query<TestData>("SELECT * FROM TestData").ToList();
            }

            System.Console.WriteLine($"数据源对象 {typeof(TestData).GetProperties().Length} 个字段,共 {datas.Count} 条记录,大小 {BinarySerializeHelper.SerializeToBytes(datas).Length/1000/1000} M");
            Task.Run(() =>
            {
                while (true)
                {
                    System.Console.WriteLine($"{DateTime.Now} 内存 : {GC.GetTotalMemory(false) / 1000 / 1000} M");
                    Thread.Sleep();
                }
            });
            Stopwatch sw = new Stopwatch();
            sw.Start();
            byte[] bytes = ExcelHandlerFactory.CreateHandler(datas).CreateExcelBytes();
            sw.Stop();
            System.Console.WriteLine($ +" 秒");
            string path = @"C:\Users\Administrator\Desktop\1.xlsx";
            FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
            fs.Write(bytes);
            fs.Dispose();
            System.Console.ReadKey();

测试结果:

就是这内存占用有点高......

源码:

using System.Collections.Generic;

namespace Wjire.Excel
{

    /// <summary>
    /// ExcelHandler工厂
    /// </summary>
    public static class ExcelHandlerFactory
    {

        /// <summary>
        /// 创建ExcelHandler
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sources">数据源</param>
        /// <param name="choosedFields">需要导出的字段,可不传,则导出所有字段</param>
        /// <returns></returns>
        public static ExcelHandler<T> CreateHandler<T>(IEnumerable<T> sources, HashSet<string> choosedFields = null)
        {
            return new ExcelHandler<T>(sources, choosedFields);
        }
    }
}
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Wjire.Excel
{

    /// <summary>
    /// 报表导出处理者
    /// </summary>
    public sealed class ExcelHandler<TSource>
    {

        /// <summary>
        /// 数据源
        /// </summary>
        private readonly IEnumerable<TSource> _sources;

        /// <summary>
        /// 需要导出的列信息
        /// </summary>
        private readonly ColumnInfo[] _columnInfos;

        /// <summary>
        /// 工作簿
        /// </summary>
        private IWorkbook _workbook;

        /// <summary>
        /// 工作页
        /// </summary>
        private ISheet _sheet;

        /// <summary>
        /// 单元格样式
        /// </summary>
        private ICellStyle _cellStyle;

        /// <summary>
        /// 单元格样式提供器
        /// </summary>
        private ICellStyleProvider _provider;

        internal ExcelHandler(IEnumerable<TSource> sources, HashSet<string> choosedFields)
        {
            _sources = sources;
            _columnInfos = GetColumnInfosOfExport(choosedFields);
        }

        /// <summary>
        /// 数据源转字节
        /// </summary>
        /// <returns></returns>
        public byte[] CreateExcelBytes()
        {
            using (var ms = CreateExcelStream())
            {
                return ms.ToArray();
            }
        }

        /// <summary>
        /// 数据源转excel流
        /// </summary>
        /// <returns></returns>
        public MemoryStream CreateExcelStream()
        {
            try
            {
                _workbook = new HSSFWorkbook();
                _cellStyle = (_provider ?? DefaultCellStyleProvider.Singleton.Value).CreateCellStyle(_workbook);
                ;
                CreateSheetWithHeader(sheetIndex);

                ;
                foreach (TSource entity in _sources)
                {
                    //03版 excel 一个 _sheet 最多 65535 行
                    )
                    {
                        sheetIndex++;
                        CreateSheetWithHeader(sheetIndex);
                        rowIndex = ;
                    }
                    CreateDataRow(rowIndex, entity);
                    rowIndex++;
                }

                MemoryStream ms = new MemoryStream();
                _workbook.Write(ms);
                return ms;
            }
            finally
            {
                _workbook?.Close();
            }
        }

        /// <summary>
        /// 创建Sheet及列头
        /// </summary>
        private void CreateSheetWithHeader(int sheetIndex)
        {
            _sheet = _workbook.CreateSheet("第 " + sheetIndex + " 页");
            //冻结首行首列
            _sheet.CreateFreezePane(, );

            IRow header = _sheet.CreateRow();
            ; i < _columnInfos.Length; i++)
            {
                ICell cell = header.CreateCell(i);
                cell.SetCellValue(_columnInfos[i].CellDisplayAttribute.Name);
                cell.CellStyle = _cellStyle;
                //自适应宽度
                _sheet.AutoSizeColumn(i);
            }
        }

        /// <summary>
        /// 创建数据行
        /// </summary>
        /// <param name="rowIndex">行索引</param>
        /// <param name="entity">数据</param>
        private void CreateDataRow(int rowIndex, object entity)
        {
            IRow dataRow = _sheet.CreateRow(rowIndex);
            ; i < _columnInfos.Length; i++)
            {
                ICell cell = dataRow.CreateCell(i);
                cell.CellStyle = _cellStyle;
                object value = _columnInfos[i].PropertyInfo.GetValue(entity, null);
                SetCellValue(value, cell);
            }
        }

        /// <summary>
        /// 设置单元格值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cell"></param>
        private void SetCellValue(object value, ICell cell)
        {
            if (value == null)
            {
                cell.SetCellValue(string.Empty);
                return;
            }
            Type type = value.GetType();
            switch (type.Name)
            {
                case "DateTime":
                case "String":
                case "Boolean":
                    cell.SetCellValue(value.ToString());
                    break;
                case "Byte":
                case "Int16":
                case "Int32":
                case "Int64":
                case "Single":
                case "Double":
                case "Decimal":
                    cell.SetCellValue(Convert.ToDouble(value));
                    break;
                default:
                    cell.SetCellValue(string.Empty);
                    break;
            }
        }

        /// <summary>
        /// 设置excel单元格样式提供器
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public ExcelHandler<TSource> SetCellStyleProvider(ICellStyleProvider provider)
        {
            _provider = provider;
            return this;
        }

        /// <summary>
        /// 获取需要导出的列信息
        /// </summary>
        /// <param name="choosedFields"></param>
        /// <returns></returns>
        private ColumnInfo[] GetColumnInfosOfExport(HashSet<string> choosedFields)
        {
            ColumnInfo[] columnInfos = ColumnInfoContainer.GetColumnInfo(typeof(TSource));

                ? columnInfos.Where(w => choosedFields.Contains(w.PropertyInfo.Name)).ToArray()
                : columnInfos;
        }
    }
}
using System.Reflection;

namespace Wjire.Excel
{

    /// <summary>
    /// 列信息
    /// </summary>
    public class ColumnInfo
    {
        internal PropertyInfo PropertyInfo { get; set; }

        internal CellDisplayAttribute CellDisplayAttribute { get; set; }

    }
}
using System;

namespace Wjire.Excel
{

    /// <summary>
    /// excel 单元格数据显示自定义特性类
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class CellDisplayAttribute : Attribute
    {

        /// <summary>
        /// 自定义列名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">自定义列名</param>
        public CellDisplayAttribute(string name)
        {
            Name = name;
        }
    }
}
using NPOI.SS.UserModel;

namespace Wjire.Excel
{

    /// <summary>
    /// 单元格样式提供器接口
    /// </summary>
    public interface ICellStyleProvider
    {
        ICellStyle CreateCellStyle(IWorkbook workbook);
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Wjire.Excel
{

    /// <summary>
    /// 数据源列信息容器
    /// </summary>
    internal static class ColumnInfoContainer
    {

        private static readonly Dictionary<Type, ColumnInfo[]> Container = new Dictionary<Type, ColumnInfo[]>();

        /// <summary>
        /// 获取数据源列信息
        /// </summary>
        /// <param name="sourceType">数据源类类型</param>
        /// <returns></returns>
        internal static ColumnInfo[] GetColumnInfo(Type sourceType)
        {
            if (Container.TryGetValue(sourceType, out ColumnInfo[] infos))
            {
                return infos;
            }

            infos = sourceType
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(propertyInfo => propertyInfo.GetCustomAttribute<CellDisplayAttribute>(true) != null)
                .Select(propertyInfo => new ColumnInfo
                {
                    PropertyInfo = propertyInfo,
                    CellDisplayAttribute = propertyInfo.GetCustomAttribute<CellDisplayAttribute>()
                }).ToArray();
            Container.Add(sourceType, infos);
            return infos;
        }
    }
}
using NPOI.SS.UserModel;
using System;

namespace Wjire.Excel
{

    /// <summary>
    /// 默认单元格样式提供器
    /// </summary>
    internal class DefaultCellStyleProvider : ICellStyleProvider
    {

        internal static Lazy<DefaultCellStyleProvider> Singleton = new Lazy<DefaultCellStyleProvider>(() => new DefaultCellStyleProvider());

        private DefaultCellStyleProvider()
        {

        }

        /// <summary>
        /// 创建单元格样式
        /// </summary>
        /// <param name="workbook"></param>
        /// <returns></returns>
        public ICellStyle CreateCellStyle(IWorkbook workbook)
        {
            ICellStyle cellStyle = workbook.CreateCellStyle();
            cellStyle.Alignment = HorizontalAlignment.Center;
            //cellStyle.VerticalAlignment = VerticalAlignment.Center;//垂直居中非常影响效率,不建议打开该功能
            IFont font = workbook.CreateFont();
            font.FontHeightInPoints = ;
            //font.Boldweight = 700;
            cellStyle.SetFont(font);

            //边框
            //cellStyle.BorderBottom = BorderStyle.Thin;
            //cellStyle.BorderLeft = BorderStyle.Thin;
            //cellStyle.BorderRight = BorderStyle.Thin;
            //cellStyle.BorderTop = BorderStyle.Thin;

            return cellStyle;
        }
    }
}

几点说明:

1.NPOI 用的最新版本:2.4.1;

2.代码中用的 HSSFWorkbook ,不仅仅是为了兼容 word2003,在测试的时候发现,如果用 XSSFWorkbook ,耗时慢了N个数量级,不知道是不是哪里姿势不对;

3.单元格的宽度只在标题栏设置了,所以导出来的Excel可能比较丑.原因是:

  1)如果根据单元格内容的长度来调整的话,由于每一个单元格内容的长度都肯能不一样,太耗时,没必要,不如鼠标点两下来得快;

  2)虽然NPOI有个功能可以在一个sheet的数据填充完后,设置单元格的宽度自适应,但是测试了下,太太太太慢了.估计是在遍历所有的单元格,一个一个计算;

  3)还有一个折中的办法,就是根据第一行数据的各个单元格内容来调整宽度,因为有些时候,数据对象每个属性的值的长度都不会差太多.但是当长度不一的时候,会让人误以为那些长的单元格的内容已经显示完了,所以也舍弃了这个功能.

4.测试的时候发现,如果把某一列的单元格设置成超链接,点击可以打开浏览器查看那种,非常非常非常非常慢.惨不忍睹.不知道是不是姿势不对,所以也舍弃了该功能.

c# NPOI 导出23万条记录耗时12秒的更多相关文章

  1. Spring Batch 读 10 万条记录,写到 MongoDB

    实践内容 从 MariaDB 一张表内读 10 万条记录,经处理后写到 MongoDB . 具体实现 1.新建 Spring Boot 应用,依赖如下: <!-- Web 应用 --> & ...

  2. SQL 从100万条记录中的到 成绩最高的记录

    从100万条记录中的到 成绩最高的记录 问题分析:要从一张表中找到成绩最高的记录并不难,有很多种办法,最简单的就是利用TOP 1 select top 1 * from student order b ...

  3. Mysql慢查询开启和查看 ,存储过程批量插入1000万条记录进行慢查询测试

    首先登陆进入Mysql命令行  执行sql      show variables like 'slow_query%';  结果为OFF 说明还未开启慢查询 执行sql     show varia ...

  4. [Python] 通过采集23万条数据,对《哪吒》影评分析

    一.说明 数据来源:猫眼: 运行环境:Win10/Python3.7 和 Win7/Python3.5: 分析工具:jieba.WorldCloud.pyecharts和matplotlib: 程序基 ...

  5. Kettle提高表输出写入速度(每秒万条记录)

    重点: ETL 优化多数在于表输入和表输出. 转自: https://blog.csdn.net/qq_37124304 https://blog.csdn.net/qq_37124304/artic ...

  6. C# 使用EPPlus 秒导出10万条数据

    1.先要引用dll文件,可以直接使用vs自带的包管理,如下图: 输入 EPPlus 我这里是安装过了的所以这里显示的是卸载而不是安装. 安装成功了之后会看到这个dll文件 代码如下: //导出Exce ...

  7. java导出excel(解决导出几万条数据内存溢出的问题)

    import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import ja ...

  8. Mysql如何快速插入100万条记录?

    1.java程序拼接insert带多个value,使一次提交多个值. 2.插入数据之前先删除索引(注意主键不能删除),然后插入数据,最后重建索引 3.可以设置手动commit,用来提高效率 4.使用批 ...

  9. ClickHouse 对付单表上亿条记录分组查询秒出, OLAP应用秒杀其他数据库

    1.  启动并下载一个clickhouse-server, By default, starting above server instance will be run as default user ...

随机推荐

  1. 分布式事务(4)---RocketMQ实现分布式事务项目

    RocketMQ实现分布式事务 有关RocketMQ实现分布式事务前面写了一篇博客 1.RocketMQ实现分布式事务原理 下面就这个项目做个整体简单介绍,并在文字最下方附上项目Github地址. 一 ...

  2. 【字符串】P2084 进制转换-C++

    题目描述 今天小明学会了进制转换,比如(10101)2 ,那么它的十进制表示的式子就是 : 1*2^4+0*2^3+1*2^2+0*2^1+1*2^0, 那么请你编程实现,将一个M进制的数N转换成十进 ...

  3. 洛谷P3877 [TJOI2010]打扫房间 解题报告

    首先整理一下条件: 1.恰好进出每个需打扫的房间各一次 2.进出每个房间不能通过同一个门 (其实前两个条件是一回事) 3.要求每条路线都是一个闭合的环线 4.每条路线经过的房间数大于2 让你在一个n* ...

  4. c语言进阶15-数据结构总结

    数据结构结论 1.阿基米德说过:“给我一个支点,我就能翘起地球”. 数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成.记为:Data_Structure=(D, ...

  5. fiddle知识点六、如何使用fiddle进行模拟弱网

    为什么要模拟弱网 随着互联网的快速发展,越来越多的应用核心功能需要网络进行实现.同一应用在2G.3G.4G和WiFi的不停网络下,响应各有不同.但是因为现在的网络普遍为4G网络,为了保证应用在不同的网 ...

  6. Flutter初体验--环境搭建

    Fluter最近火了起来,它的有点很多,今天我做一篇在Windows下安装Flutter的教程. 一.下载    无论你要安装什么软件,都要先下载下来.我用的是SourceTree,地址: https ...

  7. .net持续集成sonarqube篇之项目管理与用户管理

    系列目录 删除项目 在学习阶段,我们可能需要经常删除已构建的项目,在sonarqube中想要删除一个项目有两个入口,都在Administration导航栏内. 在项目内部的管理界面删除 如果项目处于打 ...

  8. 如何在Windows上使用Python进行开发

    本文由葡萄城技术团队于原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 一直以来C#都是微软在编程语言方面最为显著的Tag,但时至今日Python ...

  9. python课堂整理2

    一.字节和编码 1个字节是8位二进制 utf-8 表示一个中文要用3个字节 gbk 为中国汉字发明,2个字节可表示汉字 所以 utf-8 可以读gbk编码,而gbk去读utf-8 的内容会乱码 uni ...

  10. ioc和aop理解

    1.IOC 表示控制反转. 简单点说就是原来的对象是在要使用之前通过在代码里通过new Something()的方式创建出来的: IOC则是由spring容器创建同一创建,在程序要使用到该对象的时候, ...