扩展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,同时支持不同的数据源,我们还必须寻求其他的解决方案。
废话少说,先上代码:
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 }
用户信息类
2 /// 用户信息
3 /// </summary>
4 public class UserInfo
5 {
6 private static Int32 s_userId = 0;
7 public Int32 UserId { get; private set; }
8 public String UserName { get; set; }
9 public DateTime RegisterDate { get; set; }
10 public String HomePage { get; set; }
11 public Double Popularity { get; set; }
12 }
是不是非常简洁,生成的Excel甚至能够具有超链接。也许有人会说你的代码都换了好几行了,哪能算一行代码吗? 我这里换行的目的是编写的代码清晰,希望让更多的读者能够理解。 以下是ReportExcel方法的定义:
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的定义:
这个委托传入一个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
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>
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
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 { get; set; }
12 /// <summary>
13 /// 链接地址
14 /// </summary>
15 public Func<T, Object> HrefDelegate { get; set; }
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 { get; set; }
24 /// <summary>
25 /// 数据样式
26 /// </summary>
27 public String BodyStyle { get; set; }
28 /// <summary>
29 /// 数据格式化字符串
30 /// </summary>
31 public String Format { get; set; }
32 /// <summary>
33 /// 自定义列头样式
34 /// </summary>
35 public Action<WorksheetCell> CustomHeader { get; set; }
36 /// <summary>
37 /// 自定义单元格的数据
38 /// </summary>
39 public Func<T, Object> ColumnDelegate { get; set; }
40 /// <summary>
41 /// 自定义数据呈现
42 /// </summary>
43 public Action<T, WorksheetCell> CustomRenderer { get; set; }
44 }
未完待续……