zoukankan      html  css  js  c++  java
  • 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 = 0; //行标        
                    int maxRowNum = 0;
                    int minRowNum = 0;
                    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 > 0 ? 1 : 0);
                        for (int i = 0; i < index; i++)
                        {
                            m_SyncContext.Post(SetTextSafePost, string.Format("第{0}个Excel数据正在读取数据,请稍后.....", i + 1));
                            //创建工作薄
                            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 = 0;
                            minRowNum = i * count;
                            maxRowNum = (i + 1) * 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 == 0) 
                                    {
                                        headerRow = sheet.CreateRow(0);
                                        for (int colIndex = 0; colIndex < columns; colIndex++)
                                        {
                                            cell = headerRow.CreateCell(colIndex);
                                            cell.SetCellValue(reader.GetName(colIndex).Trim());
                                            cell.CellStyle = HeadercellStyle;
                                        }
                                    }                                
                                    headerRow = sheet.CreateRow(rowIndex + 1);
                                    for (int colIndex = 0; colIndex < columns; colIndex++)
                                    {
                                        cell = headerRow.CreateCell(colIndex);
                                        cell.SetCellValue(reader[reader.GetName(colIndex)].ToString());
                                        cell.CellStyle = cellStyle;
                                    }
                                    rowIndex++;                                
                                }
    
                            }
                            if (workbook.GetSheet("Sheet1").GetRow(1) != null)
                            {
                                m_SyncContext.Post(SetTextSafePost, String.Format("第{0}个Excel读取数据完毕,正在写入Excel文件中,请稍后.....", i + 1));
                                fileName = fileNames[0] + (i + 1).ToString() + "." + fileNames[1];
                                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, 0, 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 = "300000";
                txtSize.Text = "1000000";
            }
        }
    }
  • 相关阅读:
    java swagger ui 添加header请求头参数
    springboot集成swagger添加消息头(header请求头信息)
    @Valid 注解的使用
    svn创建新分支报错:svn: E155015: Aborting commit: XXX remains in conflict
    部署JAX-WS Web服务作为战争中的Apache Tomcat(Deploying JAX-WS webservice as War in Apache Tomcat)
    weblogic项目转为tomcat之后出现的问题
    杂七杂八的笔记🥤
    文档备忘录
    装备记事本
    duff's device
  • 原文地址:https://www.cnblogs.com/panbin/p/6265207.html
Copyright © 2011-2022 走看看