C#使用NPOI导出Excel

时间:2021-03-15 09:26:42

当记录数超出65536时,有两种方式处理:

一是调用WriteToDownLoad65536方法建立多个Excel。

二是调用WriteToDownLoad方法在同一个Excel中建多个Sheet。

若在同一Excel中建多个Sheet,若记录数达数十万,会导致字节流溢出的问题,解决办法是先获取MemoryStream,然后分块读取写入文件流。

需要注意的是在读取内存流的时候,一定要将内存流的位置设为0,因为在从HssfWorkBook中获取内存流时,位置已经置于最后了!若不重Position重新置为0则读取不到任何数据。

代码

C#使用NPOI导出ExcelC#使用NPOI导出Excel
using System;
using System.Collections.Generic;
using LuCeServiceWinForm.Common;
using NPOI.HSSF.UserModel;
using NPOI.HPSF;
using System.Web;
using System.IO;
using System.Data;
using NPOI.SS.UserModel;
using System.Reflection;

namespace LuCeServiceWinForm.Helper
{
    public class NPOIHelper
    {
        static HSSFWorkbook hssfworkbook;

        /// <summary>
        /// 初始化
        /// </summary>
        static void InitializeWorkbook()
        {
            hssfworkbook = new HSSFWorkbook();

            DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation();
            dsi.Company = "";
            hssfworkbook.DocumentSummaryInformation = dsi;

            SummaryInformation si = PropertySetFactory.CreateSummaryInformation();
            si.Subject = "";
            hssfworkbook.SummaryInformation = si;
        }

        /// <summary>
        /// DataTable写入Excel
        /// </summary>
        /// <param name="FileName">要保存的文件名称 eg:test.xls</param>
        /// <param name="SheetName">工作薄名称</param>
        /// <param name="dt">要写入的DataTable </param>
        public static void WriteToDownLoad(string FileName, string SheetName, DataTable dt)
        {
            string filename = FileName;
            HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
            HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", filename));
            HttpContext.Current.Response.Clear();

            //初始化Excel信息
            InitializeWorkbook();

            //填充数据
            DTExcel(SheetName, dt, null);

            HttpContext.Current.Response.BinaryWrite(WriteToStream().GetBuffer());
            HttpContext.Current.Response.End();
        }
        /// <summary>
        /// 当大于65536条记录时,表格中建多个Sheet
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="FileName">要保存的文件名称 eg:test.xls</param>
        /// <param name="SheetName">工作薄名称</param>
        /// <param name="lst">要写入的List</param>
        public static void WriteToDownLoad<T>(string FileName, string SheetName, List<T> lst, List<string> listTitle)
        {
            //初始化Excel信息
            InitializeWorkbook();
            //填充数据
            //ListExcel<T>(SheetName, lst, listTitle);
            //填充大于65536的数据
            Fill65536(SheetName, lst, listTitle);
            MemoryStream memoryStream = WriteToStream();
            FileStream fstr = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.Write);
            WriteMemoryStream(memoryStream, fstr);
        }
        public static void WriteToDownLoad<T>(string dir, string FileName, string SheetName, List<T> lst, List<string> listTitle)
        {
            try
            {
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);

                }
                //初始化Excel信息
                InitializeWorkbook();

                //填充数据
                ListExcel<T>(SheetName, lst, listTitle);
                MemoryStream memoryStream = WriteToStream();
                FileStream fstr = new FileStream(dir + "\\" + FileName + DateTime.Now.ToString("yyMMddHHmmss") + ".xls", FileMode.OpenOrCreate, FileAccess.Write);
                WriteMemoryStream(memoryStream, fstr);
            }
            catch (Exception ex)
            {
                LogHelper.CreateLog(ex);
            }
            
        }
        /// <summary>
        /// 将源内存流写入目标内存流
        /// </summary>
        /// <param name="memoryStream">源内存流</param>
        /// <param name="fileStream">目标文件流</param>
        private static void WriteMemoryStream(MemoryStream memoryStream, FileStream fileStream)
        {
            try
            {
                using (memoryStream)
                {
                    using (fileStream)
                    {
                        //流的位置一定要归零,否则啥也读不到!
                        memoryStream.Position = 0;
                        long len = memoryStream.Length;
                        byte[] buffer = new byte[1024 * 1024];//1MB=1024 * 1024
                        while (true)
                        {
                            int r = memoryStream.Read(buffer, 0, buffer.Length);
                            if (r <= 0)//表示读取到了文件的末尾
                            {
                                break;
                            }
                            else
                            {
                                fileStream.Write(buffer, 0, r);
                                double proc = (double)fileStream.Position / len;
                                LogHelper.WriteToLog("拷贝进度:" + proc * 100 + "%");
                            }

                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.CreateLog(ex);
            }
            
        }
        /// <summary>
        /// 从HssfWorkBook中获取内存流
        /// </summary>
        /// <returns></returns>
        static MemoryStream WriteToStream()
        {
            MemoryStream file = new MemoryStream();
            try
            {
                hssfworkbook.Write(file);
            }
            catch (Exception ex)
            {
                LogHelper.CreateLog(ex);
            }
            return file;
        }



        #region 数据填充部分
        /// <summary>
        /// 将DataTable数据写入到Excel
        /// </summary>
        /// <param name="SheetName"></param>
        /// <param name="dt"></param>
        /// <param name="lstTitle"></param>
        static void DTExcel(string SheetName, DataTable dt, List<string> lstTitle)
        {
            ISheet sheet1 = hssfworkbook.CreateSheet(SheetName);
            int y = dt.Columns.Count;
            int x = dt.Rows.Count;

            //给定的标题为空,赋值datatable默认的列名
            if (lstTitle == null)
            {
                lstTitle = new List<string>();
                for (int ycount = 0; ycount < y; ycount++)
                {
                    lstTitle.Add(dt.Columns[ycount].ColumnName);
                }
            }

            IRow hsTitleRow = sheet1.CreateRow(0);
            //标题赋值
            for (int yt = 0; yt < lstTitle.Count; yt++)
            {
                hsTitleRow.CreateCell(yt).SetCellValue(lstTitle[yt]);
            }

            //填充数据项
            for (int xcount = 1; xcount < x; xcount++)
            {
                IRow hsBodyRow = sheet1.CreateRow(xcount);

                for (int ycBody = 0; ycBody < y; ycBody++)
                {
                    hsBodyRow.CreateCell(ycBody).SetCellValue(dt.DefaultView[xcount - 1][ycBody].ToString());
                }
            }

        }

        private static int index = 0;
        static void Fill65536<T>(string SheetName, List<T> lst, List<string> lstTitle)
        {
            ++index;
            if (lst.Count > 10)
            {
                ListExcel<T>(SheetName + index, lst.GetRange(0, 10), lstTitle);
                lst.RemoveRange(0, 10);
                Fill65536(SheetName, lst, lstTitle);
            }
            else
            {
                ListExcel<T>(SheetName + index, lst, lstTitle);
                index = 0;
            }
        }
        static void ListExcel<T>(string SheetName, List<T> lst, List<string> lstTitle)
        {
            ISheet sheet1 = hssfworkbook.CreateSheet(SheetName);

            T _t = (T)Activator.CreateInstance(typeof(T));
            PropertyInfo[] propertys = _t.GetType().GetProperties();

            //给定的标题为空,赋值T默认的列名
            if (lstTitle == null)
            {
                lstTitle = new List<string>();
                for (int ycount = 0; ycount < propertys.Length; ycount++)
                {
                    lstTitle.Add(((System.Reflection.MemberInfo)(propertys[ycount])).Name);//获取实体中列名称,去掉列类型
                }
            }

            IRow hsTitleRow = sheet1.CreateRow(0);
            //标题赋值
            for (int yt = 0; yt < lstTitle.Count; yt++)
            {
                hsTitleRow.CreateCell(yt).SetCellValue(lstTitle[yt]);
            }

            //填充数据项
            for (int xcount = 1; xcount <= lst.Count; xcount++)
            {
                IRow hsBodyRow = sheet1.CreateRow(xcount);

                for (int ycBody = 0; ycBody < propertys.Length; ycBody++)
                {
                    PropertyInfo pi = propertys[ycBody];
                    object obj = pi.GetValue(lst[xcount - 1], null);
                    if (obj != null)
                    {
                        hsBodyRow.CreateCell(ycBody).SetCellValue(obj.ToString());
                    }
                    else
                    {
                        hsBodyRow.CreateCell(ycBody).SetCellValue("");
                    }

                }
            }

        }
        #endregion

        /// <summary>
        /// 当大于65536条记录时,建多个Excel
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="sheetName"></param>
        /// <param name="list"></param>
        /// <param name="listTitle"></param>
        public static void WriteToDownLoad65536<T>(string folder, string fileName, string sheetName, List<T> list, List<string> listTitle)
        {
            if (list.Count > 65535)
            {
                //填充
                WriteToDownLoad<T>(folder, fileName, sheetName, list.GetRange(0, 65535), listTitle);
                list.RemoveRange(0, 65535);
                //递归
                WriteToDownLoad65536<T>(folder, fileName, sheetName, list, listTitle);
            }
            else
            {
                //填充
                WriteToDownLoad<T>(folder, fileName, sheetName, list, listTitle);
            }
        }


    }
}
NPOIHelper