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 ...
随机推荐
- ServiceFabric极简文档-5.1 编程模型选择
项目中:actor用的服务是无状态服务:ASP.NET Core用的是无状态ASP.NET Core模板.
- 快速掌握mongoDB(三)——mongoDB的索引详解
1 mongoDB索引的管理 本节介绍mongoDB中的索引,熟悉mysql/sqlserver等关系型数据库的小伙伴应该都知道索引对优化数据查询的重要性.我们先简单了解一下索引:索引的本质就是一个排 ...
- [USACO09FEB]股票市场Stock Market
题意简述: 给定⼀个DDD天的SSS只股票价格矩阵,以及初始资⾦ MMM:每次买股票只能买某个股票价格的整数倍,可以不花钱,约定获利不超过500000500000500000.最⼤化你的 总获利. 题 ...
- [leetcode] 55. Jump Game (Medium)
原题 题目意思即 每一格代表你当前最多能再往后跳几次,从第一格开始,如果能跳到最后一格返回true,反之为false. 思路:用一个下标记录当前最多能跳到哪一格,遍历一遍 --> 如果当前格子不 ...
- 小白开学Asp.Net Core 《九》
小白开学Asp.Net Core <九> — — 前端篇(不务正业) 在<小白开学Asp.Net Core 三>中使用了X-admin 2.x 和 Layui将管理后端的界面重 ...
- 代码中批量执行Oracle SQL语句
今天在写一个工具(winform),作用是批量的INSERT OR UPDATE ORACLE数据库中的一个表. 执行的时候老是报错“[911] ORA-00911: invalid charact ...
- bat命令闪退问题
今天执行一个bat命令,然而会出现问题:双击执行bat命令时,命令提示符界面会闪退. 然而该bat命令在他人电脑上可正常执行,而我会遇到这种问题. 以下是我尝试的方法: 首先尝试编辑bat命令,在ba ...
- java练习---9
//程序员:罗元昊 2017.10.22 package cn.lyh; import com.rupeng.game.GameCore; public class L implements Runn ...
- static import和import的区别
import static静态导入是JDK1.5中的新特性.一般我们导入一个类都用 import com.....ClassName;而静态导入是这样:import static com.....Cl ...
- JAVA遍历机制的性能的比较
本文首发于cartoon的博客 转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/java%E9%81%8D%E5% ...