代码改变世界

扩展NPOI,一行代码导出二进制Excel(上)

2009-12-30 12:35  Zork  阅读(1600)  评论(0编辑  收藏  举报

NPOI简介:

    NPOI,顾名思义,就是POI的.NET版本。那POI又是什么呢?POI是一套用Java写成的库,能够帮助开发者在没有安装微软Office的情况下读写Office 97-2003的文件,支持的文件格式包括xls, doc, ppt等。

NPOI官方网站:npoi.codeplex.com

关于NPOI的更多教程请参考Tony的Blog:http://www.cnblogs.com/tonyqus/archive/2009/03/16/1409966.html

 

背景:

  相信在很多软件中需要将列表数据导出Excel。 将数据导出Excel的解决方案就有很多,据我所了解有生成XML文件的,也有使用微软的Office的COM组件导出Excel等等。在此,我们选中使用NPOI,它具有以下优点:

     1.  开源,发现问题可以及时修改。

     2.  生成原生的二进制Excel,可以用OleDb读取Excel中的内容,如果导出XML文件格式的Excel,OleDB是无法读取的。

   3.  相对于使用COM组件,只需要引用NPOI的类库就可以导出Excel,不需要在服务器上额外安装COM组件,对于程序部署在无法控制的服务器上,优势不言而喻。
 

目的:   

      在选择导出Excel类库后,我们接下来就要实现一行代码导出Excel的目标。有些人也许会说我们定义一个方法,内部写入导出Excel的代码,然后通过调用该方法一行代码就能够导出Excel,没错,这样也可以实现“一行代码导出Excel”的目的。但是,这样做的明显缺点是:如果据源不同,岂不是要再多一个导出Excel的方法?那我们有很多的数据源怎么办呢?所以为了到达一行代码生成Excel,同时支持不同的数据源,我们还必须寻求其他的解决方案。

     废话少说,先上代码: 

一行代码生成Excel
 1             using (FileStream fs = new FileStream(@"D:\test1.xls", FileMode.Create))
 2             {
 3                 ExcelExporter.ReportExcel(users, "用户信息", fs, c =>
 4                 {
 5                     c.For(d => d.UserId.ToString(), "用户ID");
 6                     c.For(d => d.UserName, "用户名称");
 7                     c.For(d => d.Popularity.ToString("0.00"), "人气指数");
 8                     c.For(d => d.RegisterDate.ToString("yyyy年MM月dd日"), "注册时间");
 9                     c.For(d => d.HomePage, "用户主页").Href(e => e.HomePage);
10                 });
11             }

 用户信息类

 1         /// <summary>
 2         /// 用户信息
 3         /// </summary>
 4         public class UserInfo
 5         {
 6             private static Int32 s_userId = 0;
 7             public Int32 UserId { getprivate set; }
 8             public String UserName { getset; }
 9             public DateTime RegisterDate { getset; }
10             public String HomePage { getset; }
11             public Double Popularity { getset; }
12         } 

        是不是非常简洁,生成的Excel甚至能够具有超链接。也许有人会说你的代码都换了好几行了,哪能算一行代码吗? 我这里换行的目的是编写的代码清晰,希望让更多的读者能够理解。 以下是ReportExcel方法的定义: 

ReportExcel方法定义
1  public static void ReportExcel<T>(IEnumerable<T> dataSource, 
2                         String sheetName, 
3                         Stream writer, 
4                         Action<IRootExcelColumnBuilder<T>> columns)
5             where T : class

ReportExcel方法需要4个参数。

        1. 第一个参数:IEnumabe<T>类型的数据源。这里我们将List<UserInfo>传入。

        2. 第二个参数:Excel工作簿的名称.

        3. 第三个参数:导出Excel后保存的数据流,如果你是Web程序开发,你可以把Response.OutWriter传入。

        4. 第四个参数:是一个Action<IRootExcelColumnBuilder<T>> 委托。

    a. Action<IRootExcelColumnBuilder<T>>这个是什么东西呢?我们看看微软对 Action的定义:

public delegate void Action<T>(T obj);

 这个委托传入一个T类型的参数,无类型返回。 

 

   b. IRootExcelColumnBuilder<T>的定义如下,它是一个Excel工作表的数据列生成器,用来构建Excel工作表中的数据列标题和数据行。还记得c.For(d => d.UserId.ToString(), "用户ID");这段代码吗?

      这段代码的是用来为Excel工作表生成“用户ID”列。这里的c就是类型IRootExcelColumnBuilder<UserInfo>;

      方法for就是接口IRootExcelColumnBuilder<T>的方法INestedExcelColumnBuilder<T> For(Func<T, Object> func, String name);d => d.UserId.ToString()对应于委托:Func<T, Object>,该委托传入一个参数并返回object. T就是对应的UserInfo。返回的数据也就是UserId。

IRootExcelColumnBuilder

 1     /// <summary>
 2     /// 生成工作表中的数据列
 3     /// </summary>
 4     /// <typeparam name="T"></typeparam>
 5     public interface IRootExcelColumnBuilder<T>
 6         where T : class
 7     {
 8         /// <summary>
 9         /// 生成工作表的数据列
10         /// </summary>
11         /// <param name="name">获取数据源中的属性作为单元格数据,列标题</param>
12         /// <returns></returns>
13         INestedExcelColumnBuilder<T> For(String name);
14 
15         /// <summary>
16         /// 从表达式中生成工作表的 数据列
17         /// </summary>
18         /// <param name="expression">表达式,表达式的属性名称作为列标题,表达式的值作为单元格数据</param>
19         /// <returns></returns>
20         INestedExcelColumnBuilder<T> For(Expression<Func<T, Object>> expression);
21 
22         /// <summary>
23         /// 从表达式中生成工作表的 数据列
24         /// </summary>
25         /// <param name="func">表达式的值作为单元格的数据</param>
26         /// <param name="name">列标题</param>
27         /// <returns></returns>
28         INestedExcelColumnBuilder<T> For(Func<T, Object> func, String name);
29     }

 

 我们追根溯源,看看INestedExcelColumnBuilder的具体实现,ExcelColumnBuilder<T>

1     /// <summary>
  2     /// 工作表中的单元格数据列构建器
  3     /// </summary>
  4     /// <typeparam name="T"></typeparam>
  5     public class ExcelColumnBuilder<T> : INestedExcelColumnBuilder<T>, IRootExcelColumnBuilder<T>, IEnumerable<ExcelColumn<T>>
  6         where T : class
  7     {
  8         /// <summary>
  9         /// 工作表的数据列
 10         /// </summary>
 11         private readonly List<ExcelColumn<T>> columns = new List<ExcelColumn<T>>();
 12 
 13         /// <summary>
 14         /// 工作表的当前数据列
 15         /// </summary>
 16         private ExcelColumn<T> currentColumn;
 17 
 18         /// <summary>
 19         /// 
 20         /// </summary>
 21         /// <param name="index"></param>
 22         /// <returns></returns>
 23         public ExcelColumn<T> this[int index]
 24         {
 25             get
 26             {
 27                 return columns[index];
 28             }
 29         }
 30 
 31         #region 构造器
 32         public ExcelColumnBuilder()
 33         {
 34         }
 35         #endregion
 36 
 37         /// <summary>
 38         ///  从表达式中获取属性的名称
 39         /// </summary>
 40         /// <param name="expression">表达式</param>
 41         /// <returns>得到属性名称</returns>
 42         public static String ExpressionToName(Expression<Func<T, Object>> expression)
 43         {
 44             var memberExpression = RemoveUnary(expression.Body) as MemberExpression;
 45 
 46             return memberExpression == null ? String.Empty : memberExpression.Member.Name;
 47         }
 48 
 49         /// <summary>
 50         /// 
 51         /// </summary>
 52         /// <param name="body"></param>
 53         /// <returns></returns>
 54         private static Expression RemoveUnary(Expression body)
 55         {
 56             var unary = body as UnaryExpression;
 57             if (unary != null)
 58             {
 59                 return unary.Operand;
 60             }
 61             return body;
 62         }
 63 
 64         #region IRootExcelColumnBuilder<T> 成员
 65 
 66         /// <summary>
 67         /// 生成列
 68         /// </summary>
 69         /// <param name="name">属性名称,列标题</param>
 70         /// <returns></returns>
 71         public INestedExcelColumnBuilder<T> For(String name)
 72         {
 73             currentColumn = new ExcelColumn<T> { Name = name };
 74             columns.Add(currentColumn);
 75             return this;
 76         }
 77 
 78         /// <summary>
 79         /// 生成列
 80         /// </summary>
 81         /// <param name="name">数据属性名称</param>
 82         /// <param name="type">数据类型</param>
 83         /// <returns></returns>
 84         public INestedExcelColumnBuilder<T> For(String name, DataType type)
 85         {
 86             currentColumn = new ExcelColumn<T>
 87             {
 88                 Name = name,
 89                 DataType = type
 90             };
 91             columns.Add(currentColumn);
 92             return this;
 93         }
 94 
 95         /// <summary>
 96         /// 从表达式中生成工作表的 数据列
 97         /// </summary>
 98         /// <param name="expression">表达式,表达式的属性名称作为列标题,表达式的值作为单元格数据</param>
 99         /// <returns></returns>
100         public INestedExcelColumnBuilder<T> For(Expression<Func<T, Object>> expression)
101         {
102             currentColumn = new ExcelColumn<T>
103             {
104                 Name = ExpressionToName(expression),
105                 ColumnDelegate = expression.Compile(),
106             };
107 
108             columns.Add(currentColumn);
109             return this;
110         }
111 
112         /// <summary>
113         /// 从表达式中生成工作表的 数据列
114         /// </summary>
115         /// <param name="expression">表达式,表达式的属性名称作为列标题,表达式的值作为单元格数据</param>
116         /// <param name="type">单元格数据类型</param>
117         /// <returns></returns>
118         public INestedExcelColumnBuilder<T> For(Expression<Func<T, Object>> expression, DataType type)
119         {
120             currentColumn = new ExcelColumn<T>
121             {
122                 Name = ExpressionToName(expression),
123                 DataType = type,
124                 ColumnDelegate = expression.Compile(),
125             };
126 
127             columns.Add(currentColumn);
128             return this;
129         }
130 
131         /// <summary>
132         /// 从表达式中生成工作表的 数据列
133         /// </summary>
134         /// <param name="func">表达式的值作为单元格的数据</param>
135         /// <param name="name">列标题</param>
136         /// <returns></returns>
137         public INestedExcelColumnBuilder<T> For(Func<T, Object> func, String name)
138         {
139             currentColumn = new ExcelColumn<T>
140             {
141                 Name = name,
142                 ColumnDelegate = func
143             };
144             columns.Add(currentColumn);
145             return this;
146         }
147 
148         /// <summary>
149         /// 从表达式中生成工作表的 数据列
150         /// </summary>
151         /// <param name="func">表达式的值作为单元格的数据</param>
152         /// <param name="name">列标题</param>
153         /// <param name="type">单元格数据类型</param>
154         /// <returns></returns>
155         public INestedExcelColumnBuilder<T> For(Func<T, Object> func, String name, DataType type)
156         {
157             currentColumn = new ExcelColumn<T>
158             {
159                 Name = name,
160                 DataType = type,
161                 ColumnDelegate = func
162             };
163             columns.Add(currentColumn);
164             return this;
165         }
166 
167         #endregion
168 
169         #region INestedExcelColumnBuilder<T> 成员
170 
171         /// <summary>
172         /// 格式化单元格数据
173         /// </summary>
174         /// <param name="format">格式字符串</param>
175         /// <returns></returns>
176         public INestedExcelColumnBuilder<T> Formatted(String format)
177         {
178             this.currentColumn.Format = format;
179             return this;
180         }
181 
182         /// <summary>
183         /// 设置单元格的链接地址
184         /// </summary>
185         /// <param name="href">超链接地址</param>
186         /// <returns></returns>
187         public INestedExcelColumnBuilder<T> Href(string href)
188         {
189             this.currentColumn.HrefDelegate = (e => { return href; });
190             return this;
191         }
192 
193         /// <summary>
194         /// 设置单元格的链接地址
195         /// </summary>
196         /// <param name="expression">链接地址表达式</param>
197         /// <returns></returns>
198         public INestedExcelColumnBuilder<T> Href(Func<T, Object> expression)
199         {
200             this.currentColumn.HrefDelegate = expression;
201             return this;
202         }
203 
204         /// <summary>
205         /// 自定义生成单元格
206         /// </summary>
207         /// <param name="block"></param>
208         /// <returns></returns>
209         public INestedExcelColumnBuilder<T> Do(Action<T, WorksheetCell> block)
210         {
211             currentColumn.CustomRenderer = block;
212             return this;
213         }
214 
215         /// <summary>
216         /// 自定义生成工作表的数据列头
217         /// </summary>
218         /// <param name="block"></param>
219         /// <returns></returns>
220         public INestedExcelColumnBuilder<T> Header(Action<WorksheetCell> block)
221         {
222             currentColumn.CustomHeader = block;
223             return this;
224         }
225 
226         /// <summary>
227         /// 设置数据列标题样式ID
228         /// </summary>
229         /// <param name="styleId">样式ID</param>
230         /// <returns></returns>
231         public INestedExcelColumnBuilder<T> HeaderStyle(String styleId)
232         {
233             currentColumn.HeaderStyle = styleId;
234             return this;
235         }
236 
237         /// <summary>
238         /// 设置单元格样式ID
239         /// </summary>
240         /// <param name="styleId">样式ID</param>
241         /// <returns></returns>
242         public INestedExcelColumnBuilder<T> BodyStyle(String styleId)
243         {
244             currentColumn.BodyStyle = styleId;
245             return this;
246         }
247 
248         /// <summary>
249         /// 设置单元格数据类型
250         /// </summary>
251         /// <param name="type">数据类型</param>
252         /// <returns></returns>
253         public INestedExcelColumnBuilder<T> DataType(DataType type)
254         {
255             currentColumn.DataType = type;
256             return this;
257         }
258 
259         #endregion
260 
261         #region IEnumerable<ExcelColumn<T>> 成员
262 
263         public IEnumerator<ExcelColumn<T>> GetEnumerator()
264         {
265             return this.columns.GetEnumerator();
266         }
267 
268         #endregion
269 
270         #region IEnumerable 成员
271 
272         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
273         {
274             return this.columns.GetEnumerator();
275         }
276 
277         #endregion
278     }
 

相信很多朋友看枯燥无味的代码,已经不想再看下去了。你不妨抛开杂项,关注方法INestedExcelColumnBuilder<T> For(Func<T, Object> func, String name)

 的实现,原来,该方法是将数据列的列头和获取数据的委托保存在一个ExcelColumn<T>列表中。ExcelColumn用于在循环读取IEnumaber<T>数据源生成Excel数据单元格提供支持。

ExcelColumn

 1 /// <summary>
 2     /// 工作表数据列
 3     /// </summary>
 4     /// <typeparam name="T"></typeparam>
 5     public class ExcelColumn<T>
 6     {
 7         private DataType m_type = DataType.String;
 8         /// <summary>
 9         /// 列名称
10         /// </summary>
11         public String Name { getset; }
12         /// <summary>
13         /// 链接地址
14         /// </summary>
15         public Func<T, Object> HrefDelegate { getset; }
16         /// <summary>
17         /// 数据类型
18         /// </summary>
19         public DataType DataType { get { return this.m_type; } set { this.m_type = value; } }
20         /// <summary>
21         /// 列头样式
22         /// </summary>
23         public String HeaderStyle { getset; }
24         /// <summary>
25         ///  数据样式
26         /// </summary>
27         public String BodyStyle { getset; }
28         /// <summary>
29         /// 数据格式化字符串
30         /// </summary>
31         public String Format { getset; }
32         /// <summary>
33         /// 自定义列头样式
34         /// </summary>
35         public Action<WorksheetCell> CustomHeader { getset; }
36         /// <summary>
37         /// 自定义单元格的数据
38         /// </summary>
39         public Func<T, Object> ColumnDelegate { getset; }
40         /// <summary>
41         /// 自定义数据呈现
42         /// </summary>
43         public Action<T, WorksheetCell> CustomRenderer { getset; }
44     }

 

 

 未完待续……