基于NPOI导出Excel

在上一篇文章[关于大数据的查询与导出]中,提到了使用NPOI组件导出Excel,本想上次一起分享给大家,无奈最近比较忙,今天抽空整理了下,分享出来.

  1. 预置填充模板,并且需要支持公式计算;
  2. 可导入图片;
  3. 可以追加数据形式填充表格.
  1. 简单分一下这个功能.

    1. 需要处理模板的读取,并根据模板中指定特定适配符替换模板中的数据,需要处理三种类型的单元格的格式化:散列单元格,图片单元格,数据明细区单元格;
    2. 散列单元格: 需要定义具体的模板字符串,及期望格式化后的数据,姑且将这里处理散列单元格的对象 命名为 DispersedCellFormater;
    3. 图片的导出,需要指定需要导出Excel 中的具体位置及期望填充的图片资源,姑且将这里才处理图片单元格的对象命名为 ImageCellFormater;
    4. 明细区单元格格式化,需要制定当前的列索引及每个列的这个取值函数.姑且这里将处理对象命名为DetailCellValueFormater<T>,因为了便于使用,我们引入了泛型类型;
    5. 外部调用,统一从一个入口处理,这里暂且命名为 ExportFormater;
    6. 至于散列单元格的位置,我们采用系统内置的 Point 类型来描述,明细区的取值函数使用系统定义的Func<T,object> 来描述,这里的输入类型T即为当前行数据对象;
  2. 根据以上分析,我们简单画一下这个功能的UML图,以方便理解.
  3. 有了以上分析,实现似乎就是水到渠成的事情了.
    1. 我们拟定一个接口,用于约束导出操作.

      /// <summary>
      /// 导出接口
      /// </summary>
      public interface IExport
      {
          /// <summary>
          /// 导出数据,基于模板文件处理替换后,保存为一个新文件
          /// </summary>
          /// <param name="templateFile">模板文件</param>
          /// <param name="targetFile">目标文件</param>
          /// <param name="fromater">模板格式化处理规则</param>
          void Export(string templateFile, string targetFile, ExportFormater fromater);
      
          /// <summary>
          /// 导出数据,基于模板文件处理替换后,保存为一个新文件
          /// </summary>
          /// <typeparam name="T">数据源类型</typeparam>
          /// <param name="templateFile">模板文件</param>
          /// <param name="targetFile">目标文件</param>
          /// <param name="formater">模板格式化处理规则</param>
          /// <param name="source">数据源</param>
          void Export<T>(string templateFile, string targetFile,
              ExportFormater<T> formater, IList<T> source) where T : class;
      
          /// <summary>
          /// 以追加的形式,将数据添加到已存在的文件中
          /// </summary>
          /// <typeparam name="T">数据源类型</typeparam>
          /// <param name="targetFile">目标文件</param>
          /// <param name="formater">模板格式化处理规则</param>
          /// <param name="source">数据源</param>
          void ExportByAppend<T>(string targetFile, ExportFormater<T> formater,
              IList<T> source) where T : class;
      }

    2. 散列单元格格式处理器

      /// <summary>
       /// 散列单元格数据格式器
       /// </summary>
       public class DispersedCellFormater
       {
           /// <summary>
           /// 单元格坐标
           /// </summary>
           public Point CellPoint { get; set; }
      
           /// <summary>
           /// 格式化字符串
           /// </summary>
           public string FormaterString { get; set; }
      
           /// <summary>
           /// 格式化数据
           /// </summary>
           public object CellValue { get; set; }
      
           /// <summary>
           /// 实例化
           /// </summary>
           public DispersedCellFormater()
           {
      
           }
      
           /// <summary>
           /// 实例化
           /// </summary>
           /// <param name="x">cell 横坐标</param>
           /// <param name="y">cell 纵坐标</param>
           /// <param name="formatStr">格式化字符串</param>
           /// <param name="val">替换值</param>
           public DispersedCellFormater(int x, int y, string formatStr, object val)
              {
                  this.CellPoint = new Point(x, y);
                  this.FormaterString = formatStr;
                  this.CellValue = val;
              }
       }

    3. 图片单元格格式处理器

      /// <summary>
       /// 图片Cell格式化器
       /// </summary>
       public class ImageCellFormater
       {
           /// <summary>
           /// 单元格位置
           /// </summary>
           public Point CellPoint { get; set; }
      
           /// <summary>
           /// 显示图片
           /// </summary>
           public Image Img { get; set; }
      
           /// <summary>
           /// 实例化
           /// </summary>
           public ImageCellFormater()
              {
      
              }
      
           /// <summary>
           /// 实例化
           /// </summary>
           /// <param name="x">cell横坐标</param>
           /// <param name="y">cell纵坐标</param>
           /// <param name="img">图片</param>
           public ImageCellFormater(int x, int y, Image img)
           {
               this.CellPoint = new Point(x, y);
               this.Img = img;
           }
       }

    4. 明细区单元格格式处理器

      /// <summary>
      /// 明细单元格取值规则
      /// <typeparam name="T">数据类型</typeparam>
      /// </summary>
      public class DetailCellValueFormater<T> where T : class
      {
          /// <summary>
          /// 列索引
          /// </summary>
          public int Index { get; set; }
      
          /// <summary>
          /// 取值函数
          /// </summary>
          public Func<T, object> Value { get; set; }
      
          /// <summary>
          /// 实例化
          /// </summary>
          public DetailCellValueFormater()
          {
      
          }
      
          /// <summary>
          /// 实例化
          /// </summary>
          /// <param name="index">列索引</param>
          /// <param name="val">数据</param>
          public DetailCellValueFormater(int index, Func<T, object> val)
          {
              this.Index = index;
              this.Value = val;
          }
      }

    5. 接下来一起看下导出格式处理集合的定义.

      /// <summary>
      /// 用于描述导出格式化数据
      /// </summary>
      public class ExportFormater
      {
          /// <summary>
          /// 散列单元格替换规格
          /// </summary>
          public List<DispersedCellFormater> DispersedCellFormaters { get; private set; }
      
          /// <summary>
          /// 图片单元格格式化规则
          /// </summary>
          public List<ImageCellFormater> ImageCellFormaters { get; private set; }
      
          /// <summary>
          /// 明细数据起始行索引
          /// </summary>
          public int DetailRowBeginIndex { get; set; }
      
          /// <summary>
          /// 实例化
          /// </summary>
          public ExportFormater()
          {
              DispersedCellFormaters = new List<DispersedCellFormater>();
              ImageCellFormaters = new List<ImageCellFormater>();
          }
      }
      /// <summary>
      /// 用于描述导出格式化数据,带有明细区数据取值规则
      /// </summary>
      public class ExportFormater<T> : ExportFormater where T : class
      {
          /// <summary>
          /// 明细区取值函数
          /// </summary>
          public List<DetailCellValueFormater<T>> DetailCellValueFormaters { get; private set; }
      
          /// <summary>
          /// 实例化
          /// </summary>
          public ExportFormater()
          {
              DetailCellValueFormaters = new List<DetailCellValueFormater<T>>();
          }
      }

    6. 接下来我们简单看下导出的具体实现.
      1. 散列单元格的处理

        /// <summary>
        /// 应用散列单元格格式
        /// </summary>
        /// <param name="formater">格式化规则</param>
        /// <param name="sheet">当前sheet</param>
        private static void ApplyDispersedCell(ExportFormater formater, HSSFSheet sheet)
        {
            if (formater.DispersedCellFormaters != null && formater.DispersedCellFormaters.Count > 0)
            {
                formater.DispersedCellFormaters.ForEach(r =>
                {
                    var tempRow = sheet.GetRow(r.CellPoint.X);
                    var tempCell = tempRow.GetCell(r.CellPoint.Y);
                    var txt = tempCell.StringCellValue;
                    if (string.IsNullOrWhiteSpace(txt))
                    {
                        tempCell.SetCellValue(Convert.ToString(r.CellValue));
                    }
                    else
                    {
                        //替换模板
                        tempCell.SetCellValue(txt.Replace(r.FormaterString, Convert.ToString(r.CellValue)));
                    }
                });
            }
        }

      2. 图片单元格的处理

        /// <summary>
         /// 应用图片单元格
         /// </summary>
         /// <param name="formater">格式化处理器</param>
         /// <param name="workbook"></param>
         /// <param name="sheet"></param>
         private static void ApplyImgCell(ExportFormater formater,
             HSSFWorkbook workbook, HSSFSheet sheet)
         {
             if (formater.ImageCellFormaters.Count <= 0)
             {
                 return;
             }
             var patriarch = sheet.CreateDrawingPatriarch();
             formater.ImageCellFormaters.ForEach(t =>
             {
                 if (t.Img != null)
                 {
                     var imgData = t.Img.ToByte();
        
                     //- 图片输出的位置这么计算的:
                     //- 假设我们要将图片放置于第 5(E) 列的第 2 行
                     //- 对应索引为是 4 : 1 (默认位置)
                     //- 放置的位置就等于(默认位置)到(默认位置各自加上一行、一列)
                     var imgPath = new HSSFClientAnchor(
                        1, 1,              //- 上左 到 上右 的位置,是基于下面的行列位置
                        1, 1,              //- 下左 到 下右 的位置,是基于下面的行列位置
                        t.CellPoint.Y, t.CellPoint.X,
                        t.CellPoint.Y + 3, t.CellPoint.X + 8);
        
                     //- 插入图片到 Excel,并返回一个图片的标识
                     var pictureIdx = workbook.AddPicture(imgData, NPOI.SS.UserModel.PictureType.JPEG);
                     patriarch.CreatePicture(imgPath, pictureIdx);//- 使用绘画器绘画图片
                 }
             });
         }

      3. 明细单元格处理

        /// <summary>
        /// 使用格式应用明细单元格
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="formater">数据格式化器</param>
        /// <param name="source">数据源</param>
        /// <param name="sheet">当前单元格</param>
        private void ApplyDetailCell<T>(ExportFormater<T> formater,
            IList<T> source, HSSFSheet sheet) where T : class
        {
            if (source == null || source.Count <= 0)
            {
                return;
            }
            var bgRowIndex = formater.DetailRowBeginIndex;
            sheet.InsertRow(bgRowIndex, source.Count, (HSSFRow)sheet.GetRow(bgRowIndex));
        
            //填充数据
            for (int i = 0; i < source.Count; i++)
            {
                var tempRow = sheet.GetRow(bgRowIndex + i);
                for (int j = 0; j < formater.DetailCellValueFormaters.Count; j++)
                {
                    var tempFormarter = formater.DetailCellValueFormaters[j];
                    if (tempFormarter.Value != null)
                    {
                        var tempCell = tempRow.GetCell(tempFormarter.Index);
                        SetCellValue(tempCell, tempFormarter.Value, source[i]);
                    }
                }
            }
        }
        
        /// <summary>
                /// 设置单元格值
                /// </summary>
                /// <typeparam name="T"></typeparam>
                /// <param name="cell"></param>
                /// <param name="func"></param>
                /// <param name="data"></param>
        private void SetCellValue<T>(ICell cell, Func<T, object> func, T data)
        {
            if (cell == null || func == null)
            {
                return;
            }
        
            var val = func(data);
            if (val != null)
            {
                var valType = val.GetType();
        
                switch (valType.ToString())
                {
                    case "System.String"://字符串类型
                        cell.SetCellValue(Convert.ToString(val));
                        break;
                    case "System.DateTime"://日期类型
                        cell.SetCellValue((DateTime)val);
                        break;
                    case "System.Boolean"://布尔型
                        cell.SetCellValue((bool)val);
                        break;
                    case "System.Int16"://整型
                    case "System.Int32":
                    case "System.Int64":
                    case "System.Byte":
                    case "System.Decimal"://浮点型
                    case "System.Double":
                        cell.SetCellValue(Convert.ToDouble(val));
                        break;
                    case "System.DBNull"://空值处理
                        cell.SetCellValue("");
                        break;
                    default:
                        cell.SetCellValue("");
                        break;
                }
            }
        }

      4. 最后,有了上面的支持,我们的调用接口就变得很简单了

        /// <summary>
                /// 导出数据,基于模板文件处理替换后,保存为一个新文件
                /// </summary>
                /// <typeparam name="T">数据源类型</typeparam>
                /// <param name="templateFile">模板文件</param>
                /// <param name="targetFile">目标文件</param>
                /// <param name="fromater">模板格式化处理规则</param>
                /// <param name="source">数据源</param>
         public void Export<T>(string templateFile, string targetFile,
             ExportFormater<T> formater, IList<T> source) where T : class
         {
             #region 参数验证
                    if (string.IsNullOrWhiteSpace(templateFile))
                    {
                        throw new ArgumentNullException("templateFile");
                    }
                    if (string.IsNullOrWhiteSpace(targetFile))
                    {
                        throw new ArgumentNullException("targetFile");
                    }
                    if (!File.Exists(templateFile))
                    {
                        throw new FileNotFoundException(templateFile + " 文件不存在!");
                    }
        
                    if (formater == null)
                    {
                        throw new ArgumentNullException("formater");
                    }
                    if (source == null)
                    {
                        throw new ArgumentNullException("source");
                    }
          #endregion
        
             var fileStream = new FileStream(templateFile, FileMode.Open, FileAccess.Read);//读入excel模板
             var workbook = new HSSFWorkbook(fileStream);
             var sheet = (HSSFSheet)workbook.GetSheetAt(0);       //加载第一个sheet
        
             //设置文件属性
             SetExcelProperty(workbook);
        
             //离散单元格
             ApplyDispersedCell(formater, sheet);
        
             //明细行
             ApplyDetailCell<T>(formater, source, sheet);
        
             //应用图片
             ApplyImgCell(formater, workbook, sheet);
        
             // 格式化当前sheet,用于数据total计算
             sheet.ForceFormulaRecalculation = true;
        
             using (FileStream fs = new FileStream(targetFile, FileMode.Create, FileAccess.Write))
             {
                 workbook.Write(fs);
                 fs.Flush();
             }
        
             sheet = null;
             workbook = null;
         }

结语:

  1. 这个日志本来应该是和上一篇”关于大数据的查询与导出 ”一起出来的,但最近确实太忙了,零零碎碎的今天才整理完毕.
  2. 这里仅给出了一个导出的基本方法,Export<T>(string templateFile, string targetFile, ExportFormater<T> formater, IList<T> source),至于接口中约定的其他几个处理逻辑类似,本处不再赘述.
  3. 这个文章不是什么高大上的东东,各位看官仅当做一个工具即可,园子里面有很多写类似的东东,仅想说的是这个不具备互比性,写在这里仅仅是为了给自己一个备忘,以完善自己的代码库.
时间: 2024-10-21 05:05:36

基于NPOI导出Excel的相关文章

.NET NPOI导出Excel详解

NPOI,顾名思义,就是POI的.NET版本.那POI又是什么呢?POI是一套用Java写成的库,能够帮助开发者在没有安装微软Office的情况下读写Office的文件. 支持的文件格式包括xls, doc, ppt等. 官方网站:http://npoi.codeplex.com/ nuget直接获取使用 一.NPOI生成Excel //创建工作薄 var workbook = new HSSFWorkbook(); //创建表 var table = workbook.CreateSheet(

NPOI导出Excel - 自动适应中文宽度(帮助类下载)

前言 做了好几个Excel.Word导出,用了HTTP流导出伪Excel文件.用过Office组件(这东西在生产环境下相当麻烦,各种权限,**). 最后决定使用NPOI组件来导出,好处很多很多了,这里不多说. 这篇文章呢,主要说一下Excel导出的细节以及问题. 我在制作这个Demo的时候使用的环境: Visual Studio 2010.Office 2013 .Framework .NET 3.5 .NPOI 1.2.5(至于为什么没有选最新版 稍后说) 完成后的截图 从浏览器导出的Exce

NPOI导出excel(带图片)

近期项目中用到Excel导出功能,之前都是用普通的office组件导出的方法,今天尝试用下NPOI,故作此文以备日后查阅. 1.NPOI官网http://npoi.codeplex.com/,下载最新工具包. 2.选择.net版本(例如dotnet4),引用其中的dll. 3.后台代码 using System; using System.Web; using System.Data; using System.Configuration; using System.IO; using Test

NPOI导出EXCEL 打印设置分页及打印标题

在用NPOI导出EXCEL的时候设置分页,在网上有查到用sheet1.SetRowBreak(i)方法,但一直都没有起到作用.经过研究是要设置  sheet1.FitToPage = false; 而这个属性默认是true的,怪不得一直设置都不起作用. 设置打印标题用 hssfworkbook.SetRepeatingRowsAndColumns(0, 0, 5, 0, 5); 但最新版本已经有新的方法了. Sheet sheet1 = hssfworkbook.CreateSheet("She

分享使用NPOI导出Excel树状结构的数据,如部门用户菜单权限

大家都知道使用NPOI导出Excel格式数据 很简单,网上一搜,到处都有示例代码. 因为工作的关系,经常会有处理各种数据库数据的场景,其中处理Excel 数据导出,以备客户人员确认数据,场景很常见. 一个系统开发出来,系统要运行起来,很多数据要初始化,这个时候也是需要客户提供各种业务的基础数据.客户提供的数据中,其中除了word.pdf,最常见的就是Excel. 废话不多说,直接上图上代码: 如图, 左侧三列,作为 一个系统 所有菜单的树状结构. 其他列 以用户的信息(如用户名.登录名) 作为表

共享一个MVC4通过NPOI导出excel的通用方法

1 public static System.IO.MemoryStream ExportExcel<T>(string title, List<T> objList, params string[] excelPropertyNames) 2 { 3 NPOI.SS.UserModel.IWorkbook workbook = new NPOI.HSSF.UserModel.HSSFWorkbook(); 4 NPOI.SS.UserModel.ISheet sheet = wo

Asp.Net 使用Npoi导出Excel

引言 使用Npoi导出Excel 服务器可以不装任何office组件,昨天在做一个导出时用到Npoi导出Excel,而且所导Excel也符合规范,打开时不会有任何文件损坏之类的提示.但是在做导入时还是使用OleDb的方式,这种方式的导入在服务器端似乎还是需要装office组件的,有没有不需要装组件并且能照常导入的呢? Npoi导出/下载Excel public void NpoiExcel(DataTable dt, string title) { NPOI.HSSF.UserModel.HSS

NPOI导出Excel时出现错误“Maximum column number is 255”

此错误并不是NPOI的问题,而是Excel的问题,原因是Excel的最大列255,所以导出的列超过255时就会出现此问题 但Excel2007以上并没有此问题,因为2007以上的列已经增加到16384列啦,但2003和2007的代码是不一样的 NPOI操作2007(NPOI 2.0):http://tonyqus.sinaapp.com/npoi2tutorial 如何解决此问题 1.使用2003将列限制到255之内, 2.使用2007列可以扩大到16384个,但之前是2003的代码所以更改成2

NPOI导出Excel(含有超过65335的处理情况)

NPOI导出excel的网上有很多,正好自己遇到了学习并且记录一下: 首先说明几点: 1.Excel2003及一下:后缀xls,单个sheet最大行数为65335 Excel2007 单个sheet :后缀xlsx 单个sheet 最大行数为 1048576 2.在用NPOI.dll时,导出的excel两种形式(xls,xlsx)用到的组件不一样,xls是HSSF,xlsx是XSSF, 由于某种原因我选择的是HSSF组件的: 以linq 匿名类得到数据为数据源导出Excel 类如这样的: var