一个Excel的帮助类——ExcelHelper

近日突发奇想,封装一个Excel的帮助类,好让日后做一些Excel操作时方便一点,至少导入导出会方便点吧。不过在封装过程中发现自己太差劲了,问题多多,搞这么百来行代码花了很长时间,于是写篇日志,记录一下这个ExcelHelper,也顺便记录一下封装过程中遇到的一些问题。

整个Helper中包括了读和写两部分,读是利用ADO.NET的OleDB进行读,与查询SQL Server很相像,查询语句是这种形式

SELECT * FROM [Sheet1$A1:A10]

“$”符号后面可以加一个范围,表明要读取哪一部分,如果不加的话就表明全表读取了。
下面则是读那部分的方法,一个是通用的查询,另一个则是导入

 1         /// <summary>
 2         /// 执行SQL查询一个Excel文档的内容 
 3         /// </summary>
 4         /// <param name="fileName">Excel文件名</param>
 5         /// <param name="cmdText">要执行的SQL 区域选择用 [Sheet1$A1:C7]形式</param>
 6         /// <param name="paramters">查询参数</param>
 7         /// <returns>查询结果</returns>
 8         public static DataTable ExecuteReader(string fileName, string cmdText, params OleDbParameter[] paramters)
 9         {
10             string strCon = " Provider = Microsoft.Jet.OLEDB.4.0 ; Data Source = " + fileName + ";Extended Properties='Excel 8.0;HDR=NO;IMEX=1';";
11             OleDbConnection cn = null;
12             OleDbCommand cmd = new OleDbCommand();
13             OleDbDataReader reader = null;
14             DataTable table = new DataTable();
15             try
16             {
17                 cn = new OleDbConnection(strCon);
18                 PrepareCommand(cmd, cn, cmdText, paramters);
19                 reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
20                 table.Load(reader);
21                 cmd.Parameters.Clear();
22             }
23             finally
24             {
25                 if (reader != null)
26                 {
27                     reader.Close();
28                     reader.Dispose();
29                 }
30                 if (cn != null)
31                 {
32                     cn.Close();
33                     cn.Dispose();
34                 }
35             }
36             return table;
37         }

 

 1         /// <summary>
 2         /// 把Excel的某个工作表导入到DataTable中
 3         /// </summary>
 4         /// <param name="fileName">文件完整路径</param>
 5         /// <param name="sheetName">工作表名</param>
 6         /// <param name="HasHeader">是否存在表头。若是会把工作表第一行转成DataTable的列名</param>
 7         /// <returns>导入后的DataTable</returns>
 8         public static DataTable ImportExcel(string fileName, string sheetName, bool HasHeader)
 9         {
10             DataTable table = ExecuteReader(fileName, "select * from [" + sheetName + "]", null);
11 
12             if (HasHeader)
13             {
14                 for (int i = 0; i < table.Columns.Count; i++)
15                     table.Columns[i].ColumnName = table.Rows[0][i].ToString();
16                 table.Rows.RemoveAt(0);
17             }
18 
19             return table;
20         }

其实导入也是调用了通用查询的方法,不过加了一点表头的处理罢了。

写则是利用了一个COM组件,需要添加一个dll应用,Microsoft.Office.Interop.Excel.dll。原本也想用ADO.NET的,但是那写INSERT 和 UPDATE的SQL我确实写不好,老抛异常,于是放弃了,改用COM,不过用COM好像还挺直观的。

下面也粘两段代码,一个是编辑Excel文件的(其实就涵盖了原计划中的UPDATE和INSERT的操作而已,对于整行的删除,整列的删除,合并单元格等操作,鄙人还没做到。或许以后会补充上去。)另一个是导出到Excel文件。在这部分中重命名了Microsoft.Office.Interop.Excel这个明明空间,鄙人参考网友把它重命名为Excel了。

 1         /// <summary>
 2         /// 编辑一个Excel文档
 3         /// </summary>
 4         /// <param name="fileName">Excel文件名</param>
 5         /// <param name="table">要编辑的内容</param>
 6         /// <param name="startCell">目标单元格位置</param>
 7         public static void EditExcel(string fileName, DataTable table, string startCell)
 8         {
 9             Tuple<int, int> cell = ConvertCell(startCell);
10 
11             object missing = System.Reflection.Missing.Value;
12             Excel.Application app = new Microsoft.Office.Interop.Excel.ApplicationClass();
13             app.Application.Workbooks.Add(true);
14 
15             Excel.Workbook book = null;
16             Excel.Worksheet sheet = null;
17             bool existFile=IsExistFile(fileName);
18             if (existFile)
19             {
20                 book=app.Workbooks.Open(fileName, 0, false, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true); 
21             }
22             else {
23                 book = (Excel.Workbook)app.ActiveWorkbook;
24             }
25             sheet = (Excel.Worksheet)book.ActiveSheet;
26 
27             for (int i = 0,ei=cell.Item1; i < table.Rows.Count; i++,ei++)
28                 for (int j = 0, ej = cell.Item2; j < table.Columns.Count; j++, ej++)
29                     sheet.Cells[ei, ej] = table.Rows[i][j];
30            
31 
32             if(existFile) book.Save();
33             else book.SaveCopyAs(fileName);
34             //关闭文件
35             book.Close(false, missing, missing);
36             //退出excel
37             app.Quit();
38 
39         }

 

 1         /// <summary>
 2         /// 把DataTable导出到一个Excel文件
 3         /// </summary>
 4         /// <param name="fileName">Excel文件名</param>
 5         /// <param name="table">要导出的DataTable</param>
 6         /// <param name="AddHeader">是否要增加表头</param>
 7         public static void ExportExcel(string fileName, DataTable table, bool AddHeader)
 8         {
 9 
10             object missing = System.Reflection.Missing.Value;
11             Excel.Application app = new Microsoft.Office.Interop.Excel.ApplicationClass();
12             app.Application.Workbooks.Add(true);
13             Excel.Workbook book = (Excel.Workbook)app.ActiveWorkbook;
14             Excel.Worksheet sheet = (Excel.Worksheet)book.ActiveSheet;
15 
16             if (AddHeader)
17                 for (int i = 0; i < table.Columns.Count; i++)
18                     sheet.Cells[1, i+1] = table.Columns[i].ColumnName;
19 
20             for (int i = 0, ei = AddHeader ? 2 : 1; i < table.Rows.Count; i++, ei++)
21                 for (int j = 0; j < table.Columns.Count; j++)
22                     sheet.Cells[ei, j+1] = table.Rows[i][j];
23 
24 
25             string path = fileName.Substring(0, fileName.LastIndexOf('\\') + 1).Trim('\\');
26             if (!Directory.Exists(path))
27                 Directory.CreateDirectory(path);
28 
29             book.SaveCopyAs(fileName);
30             //关闭文件
31             book.Close(false, missing, missing);
32             //退出excel
33             app.Quit();
34         }

在这部分中遇到两个问题:

第一个是调用ApplicationClass()这个构造函数时,编译会不同过,错误信息是“无法嵌入互操作类型”,只要把Microsoft.Office.Interop.Excel.dll的 嵌入互操作类型 属性设为False就行了。

第二是给交错数组sheet[,]赋值时,如果两个下标中任意一个用了0的话,运行时会抛出COMException的,异常信息是 Exception from HRESULT: 0x800A03EC 这是由于Excel的工作表第0行或第0列是Excel工作表的条标尺,如下图红框框住的两条。

 

 因此不可赋值。在循环遍历时,循环变量则要从1开始。

下面则把整个ExcelHelper的代码粘出来,欢迎大家批评指正。

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Data.OleDb;
  6 using System.Data;
  7 using System.IO;
  8 using Microsoft.Office.Interop;
  9 using Excel = Microsoft.Office.Interop.Excel;
 10 using System.Text.RegularExpressions;
 11 
 12 namespace MyHelpers.Helpers
 13 {
 14     public class ExcelHelper
 15     {
 16         #region 公共方法
 17 
 18         #region 导入
 19 
 20         /// <summary>
 21         /// 把Excel的某个工作表导入到DataTable中
 22         /// </summary>
 23         /// <param name="fileName">文件完整路径</param>
 24         /// <param name="sheetName">工作表名</param>
 25         /// <param name="HasHeader">是否存在表头。若是会把工作表第一行转成DataTable的列名</param>
 26         /// <returns>导入后的DataTable</returns>
 27         public static DataTable ImportExcel(string fileName, string sheetName, bool HasHeader)
 28         {
 29             DataTable table = ExecuteReader(fileName, "select * from [" + sheetName + "]", null);
 30 
 31             if (HasHeader)
 32             {
 33                 for (int i = 0; i < table.Columns.Count; i++)
 34                     table.Columns[i].ColumnName = table.Rows[0][i].ToString();
 35                 table.Rows.RemoveAt(0);
 36             }
 37 
 38             return table;
 39         }
 40 
 41         /// <summary>
 42         /// 把Excel的Sheet工作表导入到DataTable中
 43         /// </summary>
 44         /// <param name="fileName">文件完整路径</param>
 45         /// <param name="HasHeader">是否存在表头。若是会把工作表第一行转成DataTable的列名</param>
 46         /// <returns>导入后的DataTable</returns>
 47         public static DataTable ImportExcel(string fileName, bool HasHeader)
 48         {
 49             return ImportExcel(fileName, "sheet1$", HasHeader);
 50         }
 51 
 52         /// <summary>
 53         /// 把Excel的某个工作表导入到DataTable中
 54         /// </summary>
 55         /// <param name="fileName">文件完整路径</param>
 56         /// <returns></returns>
 57         public static DataTable ImportExcel(string fileName)
 58         {
 59             return ImportExcel(fileName, true);
 60         }
 61 
 62         #endregion
 63 
 64         #region 导出
 65 
 66         /// <summary>
 67         /// 把DataTable导出到一个Excel文件,并增加表头
 68         /// </summary>
 69         /// <param name="fileName">Excel文件名</param>
 70         /// <param name="table">要导出的DataTable</param>
 71         public static void ExportExcel(string fileName, DataTable table)
 72         {
 73             ExportExcel(fileName, table, true);
 74         }
 75 
 76         /// <summary>
 77         /// 把DataTable导出到一个Excel文件
 78         /// </summary>
 79         /// <param name="fileName">Excel文件名</param>
 80         /// <param name="table">要导出的DataTable</param>
 81         /// <param name="AddHeader">是否要增加表头</param>
 82         public static void ExportExcel(string fileName, DataTable table, bool AddHeader)
 83         {
 84 
 85             object missing = System.Reflection.Missing.Value;
 86             Excel.Application app = new Microsoft.Office.Interop.Excel.ApplicationClass();
 87             app.Application.Workbooks.Add(true);
 88             Excel.Workbook book = (Excel.Workbook)app.ActiveWorkbook;
 89             Excel.Worksheet sheet = (Excel.Worksheet)book.ActiveSheet;
 90 
 91             if (AddHeader)
 92                 for (int i = 0; i < table.Columns.Count; i++)
 93                     sheet.Cells[1, i+1] = table.Columns[i].ColumnName;
 94 
 95             for (int i = 0, ei = AddHeader ? 2 : 1; i < table.Rows.Count; i++, ei++)
 96                 for (int j = 0; j < table.Columns.Count; j++)
 97                     sheet.Cells[ei, j+1] = table.Rows[i][j];
 98 
 99 
100             string path = fileName.Substring(0, fileName.LastIndexOf('\\') + 1).Trim('\\');
101             if (!Directory.Exists(path))
102                 Directory.CreateDirectory(path);
103 
104             book.SaveCopyAs(fileName);
105             //关闭文件
106             book.Close(false, missing, missing);
107             //退出excel
108             app.Quit();
109         }
110 
111         #endregion
112 
113         #region 通用读取
114 
115         /// <summary>
116         /// 执行SQL查询一个Excel文档的内容 
117         /// </summary>
118         /// <param name="fileName">Excel文件名</param>
119         /// <param name="cmdText">要执行的SQL 区域选择用 [Sheet1$A1:C7]形式</param>
120         /// <param name="paramters">查询参数</param>
121         /// <returns>查询结果</returns>
122         public static DataTable ExecuteReader(string fileName, string cmdText, params OleDbParameter[] paramters)
123         {
124             string strCon = " Provider = Microsoft.Jet.OLEDB.4.0 ; Data Source = " + fileName + ";Extended Properties='Excel 8.0;HDR=NO;IMEX=1';";
125             OleDbConnection cn = null;
126             OleDbCommand cmd = new OleDbCommand();
127             OleDbDataReader reader = null;
128             DataTable table = new DataTable();
129             try
130             {
131                 cn = new OleDbConnection(strCon);
132                 PrepareCommand(cmd, cn, cmdText, paramters);
133                 reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
134                 table.Load(reader);
135                 cmd.Parameters.Clear();
136             }
137             finally
138             {
139                 if (reader != null)
140                 {
141                     reader.Close();
142                     reader.Dispose();
143                 }
144                 if (cn != null)
145                 {
146                     cn.Close();
147                     cn.Dispose();
148                 }
149             }
150             return table;
151         }
152 
153         /// <summary>
154         /// 执行SQL查询一个Excel文档默认工作表Sheet1的内容 
155         /// </summary>
156         /// <param name="fileName">Excel文件名</param>
157         /// <param name="cmdText">查询参数</param>
158         /// <returns>查询结果</returns>
159         public static DataTable ExecuteReader(string fileName, string cmdText)
160         {
161             return ExecuteReader(fileName, cmdText, null);
162         }
163 
164         #endregion
165 
166         #region 通用编辑
167 
168         /// <summary>
169         /// 编辑一个Excel文档
170         /// </summary>
171         /// <param name="fileName">Excel文件名</param>
172         /// <param name="table">要编辑的内容</param>
173         /// <param name="startCell">目标单元格位置</param>
174         public static void EditExcel(string fileName, DataTable table, string startCell)
175         {
176             Tuple<int, int> cell = ConvertCell(startCell);
177 
178             object missing = System.Reflection.Missing.Value;
179             Excel.Application app = new Microsoft.Office.Interop.Excel.ApplicationClass();
180             app.Application.Workbooks.Add(true);
181 
182             Excel.Workbook book = null;
183             Excel.Worksheet sheet = null;
184             bool existFile=IsExistFile(fileName);
185             if (existFile)
186             {
187                 book=app.Workbooks.Open(fileName, 0, false, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true); 
188             }
189             else {
190                 book = (Excel.Workbook)app.ActiveWorkbook;
191             }
192             sheet = (Excel.Worksheet)book.ActiveSheet;
193 
194             for (int i = 0,ei=cell.Item1; i < table.Rows.Count; i++,ei++)
195                 for (int j = 0, ej = cell.Item2; j < table.Columns.Count; j++, ej++)
196                     sheet.Cells[ei, ej] = table.Rows[i][j];
197            
198 
199             if(existFile) book.Save();
200             else book.SaveCopyAs(fileName);
201             //关闭文件
202             book.Close(false, missing, missing);
203             //退出excel
204             app.Quit();
205 
206         }
207 
208         /// <summary>
209         /// 编辑一个Excel文档
210         /// </summary>
211         /// <param name="fileName">Excel文件名</param>
212         /// <param name="table">要编辑的内容</param>
213         public static void EditExcel(string fileName, DataTable table)
214         {
215             EditExcel(fileName, table, "A1");
216         }
217 
218         #endregion
219 
220         #endregion
221 
222         #region 私有方法
223 
224         /// <summary>
225         /// 检查文件是否存在,若不存在则会先确保文件所在的目录存在
226         /// </summary>
227         /// <param name="fileName">文件名</param>
228         /// <returns>检查结果</returns>
229         private static bool IsExistFile(string fileName)
230         {
231             if(File.Exists(fileName))return true;
232             string path = fileName.Substring(0, fileName.LastIndexOf('\\') + 1).Trim('\\');
233             if (!Directory.Exists(path))
234                 Directory.CreateDirectory(path);
235             return false;
236         }
237 
238         /// <summary>
239         /// 转换单元格位置
240         /// </summary>
241         /// <param name="cell">单元格位置</param>
242         /// <returns>int二元组</returns>
243         private static Tuple<int, int> ConvertCell(string cell)
244         { 
245             Match colM=Regex.Match(cell,@"[a-zA-Z]+");
246             if (string.IsNullOrEmpty(colM.Value))
247                 throw new Exception("单元格格式有误!");
248             string colStr = colM.Value.ToUpper();
249             int colIndex = 0;
250             foreach (char ci in colStr)
251                 colIndex += 1+(ci - 'A');
252 
253             Match rowM = Regex.Match(cell, @"\d+");
254             if(string.IsNullOrEmpty(rowM.Value))
255                 throw new Exception("单元格格式有误!");
256             int rowIndex =Convert.ToInt32( rowM.Value);
257 
258             Tuple<int, int> result = new Tuple<int, int>(rowIndex,colIndex);
259             return result;
260         }
261 
262         /// <summary>
263         /// 准备OleDbCommand
264         /// </summary>
265         /// <param name="command">查询命令</param>
266         /// <param name="connection">连接类</param>
267         /// <param name="cmdText">命令内容</param>
268         /// <param name="paramters">查询参数</param>
269         private static void PrepareCommand(OleDbCommand command, OleDbConnection connection, string cmdText, params OleDbParameter[] paramters)
270         {
271             command.CommandText = cmdText;
272             command.Connection = connection;
273 
274             if (paramters != null)
275                 foreach (OleDbParameter item in paramters)
276                     command.Parameters.Add(item);
277 
278             if (connection.State !=  ConnectionState.Open)
279                 connection.Open();
280         }
281 
282         #endregion
283     }
284 }

 

 

这里有个连接通向用NPOI封装的ExcelHelper类:  另一个ExcelHelper

posted @ 2013-03-14 19:16  猴健居士  阅读(5653)  评论(10编辑  收藏  举报