Winform .NET 利用NPOI导出大数据量的Excel
前言:公司让做一个导出数据到Excel的小工具,要求是用户前端输入sql语句,点击导出按钮之后,将数据导出到Excel,界面如图所示:文件下端显示导出的进度
遇到的问题:
1、使用NPOI进行Excel 的导出,使用XSSFWorkbook导出,这个类可以使每sheet条数超过十万条,使用HSSFWorkbook每个sheet只可以导出65535条数据。
2、导出数据时,创建新的线程进行导出,防止页面卡死不动。
3、在使用线程时,如果给线程方法传递参数,此示例中主要是通过
Thread t = new Thread(new ParameterizedThreadStart(T));
t.Start(filePath);
其中,T为线程方法;filePath为传递给T方法的参数。
T方法的声明:private void T(object o){}
由此可看出,T 的方法参数必须声明为object类型
4、在导出的过程中,如果一共有130万的数据,那么需要分n个Excel导出,一个Excel放130万的数据,并不是每台电脑都可以打开。
5、比如说一个Excel放30万数据,每次查询30万的数据,查询完之后,就生成一个Excel,然后再查询第二个30万,导出第二个Excel,读取完成一个,变量马上释放,防止内存溢出。
6、使用变量时,应及时释放,而且循环中最好不要声明变量,因为循环中声明变量的话,会导致不断的给变量分配内存,如果不及时释放的话,会导致内存溢出。
7、导出数据时,另起了一个线程(方法名称为T),这个线程不能直接获得页面上的控件,原因是页面上的控件和导数据的线程不是同一个,所以无法直接获取。可以通过线程SynchronizationContext实现:
SynchronizationContext m_SyncContext = null; //声明
public Form1()
{
InitializeComponent();
//获取UI线程同步上下文
m_SyncContext = SynchronizationContext.Current;
}
导出数据的线程方法T:
private void T(object o)
{
m_SyncContext.Post(SetTextSafePost, String.Format("{0:N0}", rate * 100) + "%");
}
其中,Post方法的第一个参数为声明的方法名,第二个参数为SetTextSafePost方法的参数
SetTextSafePost方法:
private void SetTextSafePost(object o)
{
label1.Text = "文件下载进度:" + o.ToString();
}
SetTextSafePost的方法的参数也是必须为Object类型
7、在循环写入的Excel文档中时,将查询的结果集放大DataTable里不是一个好的解决方案,因为将所有的数据都放到DataTable里面是所有的数据都压到内存中,如果数据量过大的话,容易导致内存溢出。可以使用MySqlDataReader,while循环的过程中,将数据写入到Excel中。
代码示例:
using MySql.Data.MySqlClient;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms; namespace ExportDataBySQLToExcel
{
public partial class Form1 : Form
{
private string sql = "";
SynchronizationContext m_SyncContext = null;
public Form1()
{
InitializeComponent();
//获取UI线程同步上下文
m_SyncContext = SynchronizationContext.Current;
}
/// <summary>
/// 打开配置连接信息页面
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param> private void button2_Click(object sender, EventArgs e)
{
ConfigForm cf = new ConfigForm();
cf.ShowDialog(); }
/// <summary>
/// 导出按钮
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param> private void button1_Click(object sender, EventArgs e)
{
sql = txtSql.Text;
if (sql == "")
{
MessageBox.Show("sql文本不允许为空,请确认");
return;
}
if (txtCount.Text == "")
{
MessageBox.Show("最大行数不允许为空,请确认");
return;
}
if (txtSize.Text == "")
{
MessageBox.Show("总条数不能为空");
}
SaveFileDialog sfd = new SaveFileDialog();
//sfd.Filter = "Excel文件(*.xls)|*.xls|Excel文件(*.xlsx)|*.xlsx";
sfd.Filter = "Excel文件(*.xlsx)|*.xlsx"; if (sfd.ShowDialog() == DialogResult.OK)
{
button1.Enabled = false;
button2.Enabled = false;
label1.Text = "文件下载中...";
string filePath = sfd.FileName;
Thread t = new Thread(new ParameterizedThreadStart(T));
t.Start(filePath);
} }
/// <summary>
/// 线程方法
/// </summary>
private void T(object o)
{
string fileName;
string[] fileNames = o.ToString().Split('.');
XSSFWorkbook workbook = null;
try
{
//创建表
ISheet sheet = null;
ICellStyle cellStyle = null;
NPOI.SS.UserModel.IFont cellfont = null; //获得DataTable
MySqlHelper.connectionStringManager = PubClass.getConnString();
IRow headerRow = null;
ICell cell = null;
int count = int.Parse(txtCount.Text); //一个Excel的总条数 int rowIndex = ; //行标
int maxRowNum = ;
int minRowNum = ;
string mysql = "";
//string countsql = string.Format("SET @rows=0; SELECT ROWNUM FROM ({0}) AS A ORDER BY ROWNUM DESC LIMIT 1", sql, minRowNum, maxRowNum);
//DataTable dt = MySqlHelper.GetDataTable(null, CommandType.Text, countsql);
if (txtSize.Text!="")
{
int size = int.Parse(txtSize.Text); // 1130389;
//size = int.Parse(dt.Rows[0]["ROWNUM"].ToString());
int index = size / count + (size % count > ? : );
for (int i = ; i < index; i++)
{
m_SyncContext.Post(SetTextSafePost, string.Format("第{0}个Excel数据正在读取数据,请稍后.....", i + ));
//创建工作薄
workbook = new XSSFWorkbook();
sheet = workbook.CreateSheet("Sheet1");
#region 样式
ICellStyle HeadercellStyle = workbook.CreateCellStyle();
HeadercellStyle.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
HeadercellStyle.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
HeadercellStyle.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
HeadercellStyle.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
HeadercellStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
//字体
NPOI.SS.UserModel.IFont headerfont = workbook.CreateFont();
headerfont.Boldweight = (short)FontBoldWeight.Bold;
HeadercellStyle.SetFont(headerfont); cellStyle = workbook.CreateCellStyle();
//为避免日期格式被Excel自动替换,所以设定 format 为 『@』 表示一率当成text來看
cellStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat("@");
cellStyle.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
cellStyle.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
cellStyle.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
cellStyle.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin; cellfont = workbook.CreateFont();
cellfont.Boldweight = (short)FontBoldWeight.Normal;
cellStyle.SetFont(cellfont); #endregion rowIndex = ;
minRowNum = i * count;
maxRowNum = (i + ) * count;
mysql = string.Format("SET @rows=0; SELECT * FROM ({0}) AS A WHERE A.ROWNUM>{1} AND a.ROWNUM<={2}", sql, minRowNum, maxRowNum);
using (MySqlDataReader reader = MySqlHelper.ExecuteReader(MySqlHelper.connectionStringManager, CommandType.Text, mysql, null))
{
int columns = reader.FieldCount; //获得总列数
while (reader.Read())
{
if (rowIndex == )
{
headerRow = sheet.CreateRow();
for (int colIndex = ; colIndex < columns; colIndex++)
{
cell = headerRow.CreateCell(colIndex);
cell.SetCellValue(reader.GetName(colIndex).Trim());
cell.CellStyle = HeadercellStyle;
}
}
headerRow = sheet.CreateRow(rowIndex + );
for (int colIndex = ; colIndex < columns; colIndex++)
{
cell = headerRow.CreateCell(colIndex);
cell.SetCellValue(reader[reader.GetName(colIndex)].ToString());
cell.CellStyle = cellStyle;
}
rowIndex++;
} }
if (workbook.GetSheet("Sheet1").GetRow() != null)
{
m_SyncContext.Post(SetTextSafePost, String.Format("第{0}个Excel读取数据完毕,正在写入Excel文件中,请稍后.....", i + ));
fileName = fileNames[] + (i + ).ToString() + "." + fileNames[];
SaveExcel(fileName, workbook);
}
else
{
break;
} workbook = null;
}
m_SyncContext.Post(SetTextSafePost, String.Format("导出成功"));
m_SyncContext.Post(setButtonEnable, true);
MessageBox.Show("导出成功", "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
} }
catch (Exception ex)
{
m_SyncContext.Post(SetTextSafePost, String.Format("导出失败"));
m_SyncContext.Post(setButtonEnable, true);
MessageBox.Show("导出失败,提示信息为:" + ex.Message, "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
finally
{
workbook = null;
//将WorkBook写入到File中
//for (int i = 0; i < lstWorkBook.Count; i++)
//{
// fileName = fileNames[0] + i.ToString() + "." + fileNames[1];
// SaveExcel(fileName, lstWorkBook[i]);
//}
}
}
private void SaveExcel(string fileName, XSSFWorkbook workbook)
{
if (workbook != null)
{
//转为字节数组
MemoryStream stream = new MemoryStream();
workbook.Write(stream);
var buf = stream.ToArray();
//保存为Excel文件
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
fs.Write(buf, , buf.Length);
fs.Flush();
}
}
}
private void SetTextSafePost(object o)
{
label1.Text = o.ToString();
}
private void setButtonEnable(object o)
{
button1.Enabled = (bool)o;
button2.Enabled = (bool)o;
label1.Text = "文件下载完成";
} /// <summary>
/// 没用的代码
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
sql = txtSql.Text;
txtCount.Text = "";
txtSize.Text = "";
}
}
}
Winform .NET 利用NPOI导出大数据量的Excel的更多相关文章
- Struts2 利用AJAX 导出大数据设置遮罩层
Struts2 利用AJAX 导出大数据设置遮罩层 需求背景: 每次我们导出excel的时候 ,如果数据量很大,导出花费的时间会很长,页面却有没人任何反应,这个时候用户会认为系统有问题,要么关了页面, ...
- POI3.8解决导出大数据量excel文件时内存溢出的问题
POI3.8的SXSSF包是XSSF的一个扩展版本,支持流处理,在生成大数据量的电子表格且堆空间有限时使用.SXSSF通过限制内存中可访问的记录行数来实现其低内存利用,当达到限定值时,新一行数据的加入 ...
- 有效提升大数据量写入excel的效率
在开发过程中经常会有需要将数据导出到 excel 的需求,当数据量很大,达到几万甚至几十万.几百万级别的时候,如何加快生成 excel 的速度呢?首先普及一下知识背景:Excel2003 及以下版本一 ...
- poi 操作Excel 以及大数据量导出
maven 依赖 (版本必须一致,否则使用SXSSFworkbook 时程序会报错) <dependency> <groupId>org.apache.poi</grou ...
- SQL Server 使用bcp进行大数据量导出导入
转载:http://www.cnblogs.com/gaizai/archive/2010/04/17/1714389.html SQL Server的导出导入方式有: 在SQL Server中提供了 ...
- MySQL数据库如何解决大数据量存储问题
利用MySQL数据库如何解决大数据量存储问题? 各位高手您们好,我最近接手公司里一个比较棘手的问题,关于如何利用MySQL存储大数据量的问题,主要是数据库中的两张历史数据表,一张模拟量历史数据和一张开 ...
- POI读写大数据量EXCEL
另一篇文章http://www.cnblogs.com/tootwo2/p/8120053.html里面有xml的一些解释. 大数据量的excel一般都是.xlsx格式的,网上使用POI读写的例子比较 ...
- NPOI大数据量多个sheet导出源码(原)
代码如下: #region NPOI大数据量多个sheet导出 /// <summary> /// 大数据量多个sheet导出 /// </summary> /// <t ...
- (转)利用WPF的ListView进行大数据量异步加载
原文:http://www.cnblogs.com/scy251147/archive/2012/01/08/2305319.html 由于之前利用Winform的ListView进行大数据量加载的时 ...
随机推荐
- mongoDB(2)--mongoDB的常用命令
默认设置后台启动: vi mongodb.cfg 创建配置文件,配置启动信息 dbpath=/root/mongodb/data logpath=/root/mongodb/log/mongodb.l ...
- dax学习
增长率 = (DIVIDE(SUM('业绩达成'[实际业绩]),CALCULATE(SUM('业绩达成'[实际业绩]),PREVIOUSMONTH('业绩达成'[周期])))-1)*100上月业绩 = ...
- WINAPI方式在windows不同缩放比下取得正确的分辨率
作者: 国家电网河南原阳县供电公司 俏狐:86074731 这个问题我在国内外的很多论坛查资料都没有找到,只有自己研究了,希望可以帮到需要的朋友.本文为原创,转 载请注明出处. #coding:utf ...
- MYSQL临时表使用方法
当工作在非常大的表上时,你可能偶尔需要运行很多查询获得一个大量数据的小的子集,不是对整个表运行这些查询,而是让MySQL每次找出所需的少数记录,将记录选择到一个临时表可能更快些,然后在这些表运行查询. ...
- VS连接数据库字符串
在App.config配置文件中的<Configuration>节点中添加如下代码 <connectionStrings> // SQL Server 数据库 ...
- Intellij IDEA常用快捷键介绍 Intellij IDEA快捷键大全汇总
其他的快捷键还有很多,象Ctrl+G(跳转到指定行).Ctrl+F4(关闭当前编辑页面).Ctrl+F(搜索)等等,这些快捷键由于是各个编辑器都会提供的,而且定义的键位也都差不多,就没什么可说的了: ...
- linux7 安装 zlib依赖库 与安装python 3.6
Linux 安装zlib依赖库 进入src: cd /usr/local/src 下载zlib库: wget http://www.zlib.net/zlib-1.2.11.tar.gz 解压下载的t ...
- Python re.findall函数不能匹配但是notepad++能匹配
我使用同样的表达式匹配同样的网页源码,在notepad++里面不能直接使用,需要将内容都弄到同一行中. 但是我使用 requests.get(self.url).content.decode('UTF ...
- Linux命令:help
语法 help: help [-dms] [模式 ...] 显示内建命令的相关信息. 显示内建命令的简略信息.如果指定了 PATTERN 模式,给出所有匹配 PATTERN 模式的命令的详细帮助,否则 ...
- U3D学习资料收集
1,风宇冲的博客 2,gkEngine 3,@浅墨_毛星云 4,聊聊引擎底层如何实现BRDF渲染算法