c# NPOI 导出23万条记录耗时12秒
先上测试代码:
- 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秒的更多相关文章
- Spring Batch 读 10 万条记录,写到 MongoDB
实践内容 从 MariaDB 一张表内读 10 万条记录,经处理后写到 MongoDB . 具体实现 1.新建 Spring Boot 应用,依赖如下: <!-- Web 应用 --> & ...
- SQL 从100万条记录中的到 成绩最高的记录
从100万条记录中的到 成绩最高的记录 问题分析:要从一张表中找到成绩最高的记录并不难,有很多种办法,最简单的就是利用TOP 1 select top 1 * from student order b ...
- Mysql慢查询开启和查看 ,存储过程批量插入1000万条记录进行慢查询测试
首先登陆进入Mysql命令行 执行sql show variables like 'slow_query%'; 结果为OFF 说明还未开启慢查询 执行sql show varia ...
- [Python] 通过采集23万条数据,对《哪吒》影评分析
一.说明 数据来源:猫眼: 运行环境:Win10/Python3.7 和 Win7/Python3.5: 分析工具:jieba.WorldCloud.pyecharts和matplotlib: 程序基 ...
- Kettle提高表输出写入速度(每秒万条记录)
重点: ETL 优化多数在于表输入和表输出. 转自: https://blog.csdn.net/qq_37124304 https://blog.csdn.net/qq_37124304/artic ...
- C# 使用EPPlus 秒导出10万条数据
1.先要引用dll文件,可以直接使用vs自带的包管理,如下图: 输入 EPPlus 我这里是安装过了的所以这里显示的是卸载而不是安装. 安装成功了之后会看到这个dll文件 代码如下: //导出Exce ...
- java导出excel(解决导出几万条数据内存溢出的问题)
import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import ja ...
- Mysql如何快速插入100万条记录?
1.java程序拼接insert带多个value,使一次提交多个值. 2.插入数据之前先删除索引(注意主键不能删除),然后插入数据,最后重建索引 3.可以设置手动commit,用来提高效率 4.使用批 ...
- ClickHouse 对付单表上亿条记录分组查询秒出, OLAP应用秒杀其他数据库
1. 启动并下载一个clickhouse-server, By default, starting above server instance will be run as default user ...
随机推荐
- python基础知识四 小数据池,深浅拷贝,集合+菜中菜
四.小数据池,深浅拷贝,集合+菜中菜 1小数据池 --缓存机制(驻留机制) '==' 判断两边内容是否相等 'is' 基于内存地址进行判断是否相同 a = 10 b = 10 print(a ...
- RabbitMQ(二):RabbitMQ高级特性
RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用.作为一名合格的开发者,有必要了解一下相关知识,RabbitMQ(一)已经入门RabbitMQ,本文介绍Rabb ...
- JSON的简单使用之提取多层嵌套的JSON(C#)
JSON.NET(http://json.codeplex.com/)使用来将.NET中的对象转换为JSON字符串(序列化?),或者将JSON字符串转换为.NET中已有类型的对象(反序列化?) 反序列 ...
- MacBook Air多出一块磁盘?
今天将MAC的系统升级到Mojave,启动之后发现系统挂载的磁盘变了,我记得升级之前文件系统是挂载在/dev/disk0上的,但是升级之后,文件系统挂载在/dev/disk1上了. 用diskutil ...
- 我是这样一步步理解--主题模型(Topic Model)、LDA
1. LDA模型是什么 LDA可以分为以下5个步骤: 一个函数:gamma函数. 四个分布:二项分布.多项分布.beta分布.Dirichlet分布. 一个概念和一个理念:共轭先验和贝叶斯框架. 两个 ...
- JSP使用分层实现业务处理
在Java开发中,使用JDBC操作数据库的四个步骤如下: ①加载数据库驱动程序(Class.forName("数据库驱动类");) ②连接数据库(Connection co ...
- activeMQ_helloworld(一)
一.activeMQ下载,直接在Linux上wget http://mirror.bit.edu.cn/apache//activemq/5.14.5/apache-activemq-5.14.5-b ...
- MyBatis框架之关联查询
概述:关联查询主要在<resultMap>元素中,用<association>配置一对一.用<collection> 配置一对多 一.一对一查询 1.使 ...
- 认识 tomcat 被占用问题
(1) Server 中的 port 该端口为tomcat使用jvm的端口,必须保证唯一性,否则tomcat启动不成功: (2) Connector 中的 port 该端口为tomcat中所有web应 ...
- Java动态,安全追踪工具
Java动态,安全追踪工具 在我们日常的开发中,总是难以避免的要解决线上的问题.如果线上的问题我们在本地调试的时候无论调试多少次发现明明本地调用了这个方法呀,怎么线上就是没调呢?还有就是出了问题的时候 ...