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 ...
随机推荐
- 基于SpringCloud的微服务架构实战案例项目,以一个简单的购物流程为示例
QuickStart 基于SpringCloud体系实现,简单购物流程实现,满足基本功能:注册.登录.商品列表展示.商品详情展示.订单创建.详情查看.订单支付.库存更新等等. 每个业务服务采用独立的M ...
- ecshop面包屑修改
找到includes 找到lib_main.php 大约163样左右 /* 处理有分类的 */这段代码下面的一行修改成的对应的自己网站的分类,类似这样: 注释掉180行到194行左右,然后添加自己的分 ...
- 不要天真了,这些简历HR一看就知道你是培训的,质量不佳的那种
上到职场干将下到职场萌新,都会接触到包装简历这个词语.当你简历投到心仪的公司,公司内负责求职的工作人员是如何甄别简历的包装程度的?Jason 老师根据自己的经验写下了这篇文章,谁都不是天才,包装无可厚 ...
- ansible安装应用软件
1.创建相应的目录: mkdir -p /ansible/roles/{nginx,mysql,tomcat,db,zabbix}/{defaults,files,handlers,meta,task ...
- BZOJ3033 太鼓达人题解
太鼓达人 时间限制: 1 Sec 内存限制: 128 MB 题目描述 七夕祭上,Vani牵着cl的手,在明亮的灯光和欢乐的气氛中愉快地穿行.这时,在前面忽然出现了一台太鼓达人机台,而在机台前坐着的是 ...
- infiniband 网卡驱动安装
硬件:Mellanox InfiniBand,主要包括 HCA(主机通道适配器)和交换机两部分软件:CentOS 6.4MLNX_OFED_LINUX-2.1-1.0.0-rhel6.4-x86_64 ...
- 打开pycharm,提示invalid Log Path【已解决】
问题:打开pycharm,提示invalid Log Path 解决: 网上其他方法都说重装,这个成本有点高,所以我不去尝试. 因为我下载的是免安装版,所以使用时生成的文件是后来才生成的,所以我尝试 ...
- Android 开发感想
18年从.net转行做安卓开发,现在已经过去一年多了.说一下感想和心得体会! 一.开始 说一下我的经厉,从毕业开始出来工作一直是从事.net方向的开发工作.一开始也是没什么经验,加上也没有其他手艺就找 ...
- c++小游戏——拯救公主
#include<stdio.h> #include<ctime> #include<time.h> //suiji #include<windows.h&g ...
- HDFS读写数据流程
HDFS的组成 1.NameNode:存储文件的元数据,如文件名,文件目录结构,文件属性(创建时间,文件权限,文件大小) 以及每个文件的块列表和块所在的DataNode等.类似于一本书的目录功能. 2 ...