前些天写了个导出Excel的公共类,因为项目中也需要上传Excel,没有利用Office组件,(PS:Office在上传文件时候,Excel进程无法关闭,会导致上传和打开失败)有的说利用Kill把进程给关闭,我总觉得这样的方法不好,没有任何原因,关闭一个进程,想想都有点悬。

这个例子是采用NPOI组件,下载地址:http://npoi.codeplex.com/,先不说组件的事,说下实现思路。

把类实体转换表格时候,最头疼莫过于表头和字段的设计,刚开始有几个同事利用解析Html,有几个导出功能就写几个导出函数,最后函数多了就直接构建一个类,这样功能也能在实现,但不是个很好的选择。

在类转为表的时候我们可以考虑利用反射功能把类的属性和值都取出来,这个的话,我们只需要构建一个函数,利用泛型把相应的类型传入就行。

下面我们可以定义一个函数的签名:

public static void ExportExcel<T>(List<T> tList)
{
}

好了,这样的话T就是我们要处理的类型,利用反射取出字段和值

XSSFWorkbook xssfWorkbook = new XSSFWorkbook();//定义一个工作薄
XSSFSheet xssfSheet = (XSSFSheet)xssfWorkbook.CreateSheet("sheet1");//定义一个表单
NPOI.SS.UserModel.IRow titleRow = xssfSheet.CreateRow();//创建一个行
PropertyInfo[] propertyInfos = typeof(T).GetProperties();//取出字段数组
;//定义索引
  foreach (var propertyInfo in propertyInfos)//对数组进行遍历
      {
            titleRow.CreateCell(index++).SetCellValue(propertyInfo. Name);//赋值
     }

我们这样已经把字段取出来了,但是现现在的字段就类的字段,有的时候我们需要定义我们想要的表头,大多数的表头还是中文,还有的我也不要全部反射出来?这个怎么办?(解决方法在后面,千万不要放弃阅读)

好了,表头基本上已经搞定,还有一个悬念,后面我会说。下面是取出数据

], null);//取出第0 行数据
if (obj != null)
{
Row.CreateCell(index++).SetCellValue(obj.ToString());//设定到相应的列}
else
{
            index++;
}

一个字段的值取出来了,下面加快工作,代码如下:

 ;j<tList.Count();j++)//传入的List遍历
            {
                NPOI.SS.UserModel.IRow Row = xssfSheet.CreateRow(j+);//创建列

               index = ;//重新设定索引

                foreach (var propertyInfo in propertyInfos)
                {
                    Object[] exportExcelAttributes = propertyInfo.GetCustomAttributes(true);
                    )
                    {
                        foreach (var exportExcelAttribute in exportExcelAttributes)
                        {
                            var attribute = exportExcelAttribute as ExportExcelAttribute;
                            if (attribute != null)
                            {
                             object obj = propertyInfo.GetValue(tList[j], null);//取出数据
                                if (obj != null)
                                {
                                    Row.CreateCell(index++).SetCellValue(obj.ToString());//设定到相应的列
                                }
                                else
                                {
                                    index++;
                                }
                            }
                        }
                    }
                }
            }

到目前已经把一个类实体转为一个Excel了,下面开始解决刚才的那个的疑问。有的表头是中文,有的我想设定的表头和字段不一样怎么办?还有字段我不想全部导出怎么办?

这个时候我们可以考虑使用Attribute (特性),我们可以自定一个特性。

所有特性类型都直接或间接地从 Attribute 类派生。 特性可应用于任何目标元素;多个特性可应用于同一目标元素;并且特性可由从目标元素派生的元素继承。 使用AttributeTargets 类可以指定特性所应用到的目标元素。 -----MSDN

这个话不读也没事,我们只需要第一句,我们要派生一个Attribute类,代码如下:

[AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
   public class ExportExcelAttribute:Attribute
    {
       private readonly string titleName;

       public ExportExcelAttribute(string name)
       {
           titleName = name;
       }

       public string TitleName
       {
           get { return titleName; }
       }
    }

代码简单,不多说。这样的话类实体也要修改下,先看下类实体的变化:

public class DriverInfo : BaseEntity
    {
        [ExportExcel("姓名")]
        public string name { get; set; }
        public string DriverInfoCode { get; set; }
        [ExportExcel("备注")]
        public string note { get; set; }
        [ExportExcel("身份证号")]
        public string IDCardNum { get; set; }

        public string DriverNum { get; set; }//驾驶证号
        [ExportExcel("出生日期")]
        public DateTime? BirthDay { get; set; }
        [ExportExcel("性别")]
        public string Sex { get; set; }
        public string Location { get; set; }//归属地
    }

看了就该有了三分明白了,我们要在需要导出的字段上加上    [ExportExcel("出生日期")],用来和其它字段区别,有这个特性的我们导出,按着后面给出的名称来设定。下面工作又要回到反射上面:

生成表头:

    foreach (var propertyInfo in propertyInfos)//对数组进行遍历
            {
                Object[] exportExcelAttributes = propertyInfo.GetCustomAttributes(true);
                )
                {
                    foreach (var exportExcelAttribute in exportExcelAttributes)
                    {
                        var attribute = exportExcelAttribute as ExportExcelAttribute;
                        if (attribute != null)
                        {
                            titleRow.CreateCell(index++).SetCellValue(attribute.TitleName);
                        }
                    }
                }
            }
    

取值也是同样,代码如下:

  ;j<tList.Count();j++)//传入的List遍历
            {
                NPOI.SS.UserModel.IRow Row = xssfSheet.CreateRow(j+);//创建列

               index = ;//重新设定索引

                foreach (var propertyInfo in propertyInfos)
                {
                    Object[] exportExcelAttributes = propertyInfo.GetCustomAttributes(true);
                    )
                    {
                        foreach (var exportExcelAttribute in exportExcelAttributes)
                        {
                            var attribute = exportExcelAttribute as ExportExcelAttribute;
                            if (attribute != null)
                            {
                             object obj = propertyInfo.GetValue(tList[j], null);//取出数据
                                if (obj != null)
                                {
                                    Row.CreateCell(index++).SetCellValue(obj.ToString());//设定到相应的列
                                }
                                else
                                {
                                    index++;
                                }
                            }
                        }
                    }
                }
            }
   这样的话每次要导出数据的时候,我们只需要修改类实体就行了,而不用去做不同的处理。到此,我们的工作完成一半,说好的导出呢?

下面可以来说下导出:说下思路,Excel构建完成后,先把它保存服务器上面的一个相对路径,然后通过Response.Flush();实现下载。

重构下函数签名:

public static void ExportExcel<T>(List<T> tList, HttpContext httpContext)
{
}

代码:

    string DownPath=httpContext.Server.MapPath("~/DownloadPath");//服务器的相对径
            MemoryStream memoryStream = new MemoryStream();
        xssfWorkbook.Write(memoryStream);
            string fileName = DownPath + "\\" + DateTime.Now.ToString("yyyyMMddHHssmm") + "_"+typeof(T).Name + ".xlsx";  //定义文件名称和路径
        File.WriteAllBytes(fileName, memoryStream.ToArray());//把内存内容写入文件

下面工作就是把文件读取,实现下载:

      string fName = new FileInfo(fileName).Name;
            FileStream fs = new FileStream(fileName, FileMode.Open);
            byte[] bytes = new byte[(int)fs.Length];
            fs.Read(bytes, , bytes.Length);
            fs.Close();
            httpContext.Response.Charset = "UTF-8";
            httpContext.Response.ContentEncoding = System.Text.Encoding.GetEncoding("UTF-8");
            httpContext.Response.ContentType = "application/octet-stream";

            httpContext.Response.AddHeader("Content-Disposition", "attachment; filename=" + fName);
            httpContext.Response.BinaryWrite(bytes);
            httpContext.Response.Flush();
            httpContext.Response.End();//下载

            if (File.Exists(fileName))//完成后删除
            {
                File.Delete(fileName);
            }
      </pre><pre code_snippet_id="371974" snippet_file_name="blog_20140531_9_5695766" name="code" class="csharp">

导出结果:

到此,这个功能算是写完了,我制作个Demo下载:

http://download.csdn.net/detail/fw199006/7430623

有兴趣加群    IT项目 324110381




												

第十篇 一个利用反射实现的Excel导出的更多相关文章

  1. Java开发笔记(七十九)利用反射技术操作私有属性

    早在介绍多态的时候,曾经提到公鸡实例的性别属性可能被篡改为雌性,不过面向对象的三大特性包含了封装.继承和多态,只要把性别属性设置为private私有级别,也不提供setSex这样的性别修改方法,那么性 ...

  2. 一个很好的通用 excel 导出工具类

    此类用主要 jxl +注解+流 实现扩展性很强,jxl性能会比poi好一点,值得我们学习. package oa.common.utils; import java.io.OutputStream; ...

  3. 一个把数据转化成Excel导出的程序 python Django

    把从数据库查询出来数据导出 源码下载!!!!! 效果图 登入界面 主页面 查询到数据 导出 打开得到文件 项目地址,源码下载

  4. 利用反射跟自定义注解拼接实体对象的查询SQL

    前言 项目中虽然有ORM映射框架来帮我们拼写SQL,简化开发过程,降低开发难度.但难免会出现需要自己拼写SQL的情况,这里分享一个利用反射跟自定义注解拼接实体对象的查询SQL的方法. 代码 自定义注解 ...

  5. Python基础篇【第3篇】: Python异常处理、反射、动态导入、利用反射的web框架

    异常处理 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行. 一般情况下,在Python无法正常处理程序时就会发生一个异常.异常是Python对象,表示一个错误.当P ...

  6. C# 调用一个按钮的Click事件(利用反射)

    最基本的调用方法 (1)button1.PerformClick();(2)button1_Click(null,null);(3)button_Click(null,new EventArgs()) ...

  7. Java开发笔记(八十)利用反射技术操作私有方法

    前面介绍了如何利用反射技术读写私有属性,不单是私有属性,就连私有方法也能通过反射技术来调用.为了演示反射的逆天功能,首先给Chicken鸡类增加下列几个私有方法,简单起见弄来了set***/get** ...

  8. 利用反射--调用一个按钮的Click事件

    最基本的调用方法 (1)button1.PerformClick();(2)button1_Click(null,null);(3)button_Click(null,new EventArgs()) ...

  9. C#利用反射,遍历获得一个类的所有属性名,以及该类的实例的所有属性的值

    转自goldeneyezhang原文 C#利用反射,遍历获得一个类的所有属性名,以及该类的实例的所有属性的值 C#利用反射,遍历获得一个类的所有属性名,以及该类的实例的所有属性的值总结: 对应某个类的 ...

随机推荐

  1. c++ split()实现

    在c++中,没有java与python中定义的split()功能的函数,于是自己实现之. 情况1,适用范围,分隔符为字符.思路,记录分隔符的位置,判断需要截取的字符串的下标范围. vector< ...

  2. windows批处理實例

    實例: 假設我們有個資料夾為d:\tmp和e:\tmp ,而我們只要將d:\tmp中有異動的檔案複製到e:\tmp下的話,用法如下 xcopy d:\tmp\. e:\tmp\ /D /S /Y 實例 ...

  3. Bmob 之 简单使用

    1. pod pod 'BmobSDK' 与 pod "BmobSDK" 好像没什么区别 2. 导入 在AppDelegate中: #import <BmobSDK/Bmob ...

  4. Iframe 自适应高度并实时监控高度变化的js代码

    不得不用到iframe,且被强烈要求不能让它出现滚动条!嵌入的页面肯定是高度不一的,页面中也不能出现大片空白,所以也不能写死高度!真是麻鬼烦啊!google N次 + 百度M次 + 试验了1605次之 ...

  5. jQuery查询性能考虑

    http://blog.163.com/neusoft_hao@yeah/blog/static/120544724201282810510215/

  6. QJsonObject和QJsonArray的巨坑

    最近用Qt的QJsonObject和QJsonArray当做类变量来存储运行信息,发现这两货真的是巨坑.让人有一种JJ fly的感觉/(ㄒoㄒ)/~~. 写了个例子来说明下: MainWindow:: ...

  7. js json和字符串的互转

    json与字符串的互转:    下面格式两种只是针对引号有稍微的区别 var result = JSON.parse("{\"a\":\"擦擦\",\ ...

  8. [html5] 学习笔记-应用缓存与Web workers

    1.应用缓存 HTML5引入了应用缓存程序,这意味着Web应用可进行缓存,并可在没有因特网连接时访问. 应用缓存的优势: 1)离线浏览--用户可在应用离线时使用它们 2)速度--已缓存是从本地加载,加 ...

  9. Linux下JDK安装

    最近在学习linux,倒腾了半天.终于把jdk安装好了,写篇博客记录下来,关键是jdk环境变量的配置和保存了 我用的SecureCRT连接linux,然后再在SecureCRT上敲命令,关于Secur ...

  10. 《JavaScript DOM 编程艺术 》 笔记

    一:这本书由几个案列带入知识点,通俗易懂.最大的收获莫过于作者多次提到的逐渐增强和平稳退化. "渐进增强"指的是给所用用户同等的基本使用体验,再根据用户终端的级别给予更高级的用户更 ...