说起Excel几乎是人人熟知的,一般人都会用他来记录一些数据,这种方式很方便他们的使用,他们可以很方便的对这些数据进行操作。可现在问题就来了,为了方便用户的这种需求,开发人员就必须把他们的数据变成自己的程序代码可以操作的数据,并且将数据操作完成以后,还要生成客户需要的数据。那么在这个过程中就涉及到了,Excel表的数据与数据库表数据的交互问题。在这里我只针对SQL数据库来说明我对Excel数据的操作
首先我对自己的思路做一个整体的概述:
我在这里做一个公共实现方法:(具体代码是在放在一个类ImportExcel)
实现思路:
(1)将要导入的Excel表格上传到服务器路径的临时存储文件夹,并保存该路径
(2)以该路径打开刚刚导入的Excel文件
(3)检查该Excel文件是否为空值,若为空则提示用户“该文件为空,数据导入失败”
(4)并在此时关闭对Excel的打开连接,删除该Excel文件,返回到导入页面,显示错误信息!
(5)若检查该文件不为空,则获取该Excel文件的Sheet表名,注意这里的Sheet表的名称应该与你要导入的目标表的数据表名相同,若检查初该Excel文件中没有与目标数据表名相匹配的Sheet表名,则提示用户“该文件中不存在目标表名,请检查后再导入”重复(4)
(6)若判断该Sheet表名存在,则去获取Sql Server数据库中目标表的模式(Schema)同时也去获得Excel中该Sheet表的模式(Schema),以Sheet表的的模式参考,首先是检查Sheet表中的列名及其顺序数据库中表是不是对应的,(为了确保这里的数据导入成功,必须检查列的顺序是否对应,意即Sheet表中某字段名是第一列则他在数据库的表中也必须是第一列)
(7)若检查字段顺序不对应,则提示用户“该Excel表中的列名顺序与与数据库不对应,请检查后再导入”,重复(4)
(8)若列名顺序对应,但数据库表中还存在其他列名,但这些列名是与用户导入数据无关的,那么此时就必须去遍历这些列,并为这些列附上相应的值,当然这些值必须保证数据库数据的正确性,那么关于这些值,可以先检查其对应字段的类型,编写一些对应数据类型的固定赋值模式,然后对该字段附上固定的值。(这里实际是以用户为核心来考虑的,先保证数据能以最大可能成功导入到数据库,但要注意,附上了固定的值以后,你还得再单独用语句将其修改为正确的值)
(9)当这些操作均完成时,提示用户“数据导入成功”重复(4)
这是我在做Excel导入到数据库时,逐步完善总结出来的一些经验,某些地方可能也不免有些不完善,但大体上的功能都能实现,现在拿出来和大家交流交流,若大家有更好的方法我也乐于学习下。
关于公共类的方法接口:(这是放到公共类的模块,这样在多个页面只需调用函数即可)
1 public class ImportExcel
2 {
3 public string TransferData(string excelFile, string sheetName, string connectionString)
4 {
5 DataSet ds = new DataSet();
6 try
7 {
8 //建立Excel连接
9 string strCOnn= "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + excelFile + ";" + "Extended Properties=Excel 8.0;";
10 OleDbConnection cOnn= new OleDbConnection(strConn);
11 conn.Open();
12
13
14 //获取Excel的Sheet表名
15 DataTable dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
16 String[] excelSheets = new String[dt.Rows.Count];
17 int k = 0;
18 foreach (DataRow row in dt.Rows)
19 {
20 excelSheets[k] = row["TABLE_NAME"].ToString();
21 k++;
22 }
23
24 #region
25 //获取Excel的表的列名
26 OleDbDataAdapter oleCommand = null;
27 string oleExcel = "";
28 int n = 0;
29 oleExcel = string.Format("select * from [{0}$]", sheetName);
30 oleCommand = new OleDbDataAdapter(oleExcel, strConn);
31 DataTable oleschemaTable = null;
32 OleDbCommand olecmd = conn.CreateCommand();
33 olecmd.CommandText = oleExcel;
34 olecmd.ExecuteNonQuery();
35 OleDbDataReader ole_rdr = olecmd.ExecuteReader(System.Data.CommandBehavior.SchemaOnly);
36 oleschemaTable = ole_rdr.GetSchemaTable();
37 String[] exl_TableCloumn = new String[oleschemaTable.Rows.Count];
38 foreach (DataRow row in oleschemaTable.Rows)
39 {
40 exl_TableCloumn[n] = row["ColumnName"].ToString();
41 n++;
42 }
43 #endregion
44
45 #region
46 //读取Sql数据库中表的列名(设计模式)
47 System.Data.SqlClient.SqlConnection sqlcon = new System.Data.SqlClient.SqlConnection(connectionString);
48 sqlcon.Open();
49 int m = 0;
50 string sql = "select * from " + sheetName;
51 DataTable schemaTable = null;
52 SqlCommand cmd = sqlcon.CreateCommand();
53 cmd.CommandText = sql;
54 cmd.ExecuteNonQuery();
55 SqlDataReader rdr = cmd.ExecuteReader(System.Data.CommandBehavior.SchemaOnly);
56 schemaTable = rdr.GetSchemaTable();
57 String[] sqlTableCloumnName = new String[schemaTable.Rows.Count];
58 String[] sqlTableCloumnSize = new String[schemaTable.Rows.Count];
59 String[] sqlTableCloumnType = new String[schemaTable.Rows.Count];
60 foreach (DataRow row in schemaTable.Rows)
61 {
62 sqlTableCloumnName[m] = row["ColumnName"].ToString();//列名
63 sqlTableCloumnSize[m] = row["ColumnSize"].ToString();//
64 sqlTableCloumnType[m] = row["DataType"].ToString();//字段类型
65 m++;
66 }
67 sqlcon.Close();
68 #endregion
69
70 #region
71 //比较两数据表的中列名及其顺序是否对应
72 string tableInfo = "";
73 int i, j, p;
74 //读取数据库字段的正确顺序
75 for (i = 0; i76 {
77 tableInfo = tableInfo + sqlTableCloumnName[i].ToString() + ",";
78
79 }
80 j = sqlTableCloumnName.Length - exl_TableCloumn.Length;
81 //检查Excel表中的字段顺序是否与数据库中匹配
82 for (i = 0; i83 {
84 if (exl_TableCloumn[i].ToString() == sqlTableCloumnName[i].ToString())
85 {
86 continue;
87 }
88 else
89 {
90 conn.Close();
91 return "出错了!您提供的数据表的列名或列名的顺序有误!正确信息为:" + tableInfo;
92 }
93 }
94 #endregion
95
96 #region
97 //将Excel表中数据填充到ds中
98 OleDbDataAdapter myCommand = null;
99 string strExcel = "";
100 strExcel = string.Format("select * from [{0}$]", sheetName);
101 myCommand = new OleDbDataAdapter(strExcel, strConn);
102 myCommand.Fill(ds, sheetName);
103 #endregion
104
105
106 #region
107 //在DataSet中动态增加列并对其赋值
108 int a;
109 for (p = 0; p110 {
111 a = 0;
112 ds.Tables[0].Columns.Add(sqlTableCloumnName[i].ToString(), Type.GetType(sqlTableCloumnType[i].ToString()));
113 if (sqlTableCloumnType[i].ToString() == "System.string")
114 {
115 foreach (DataRow row in ds.Tables[0].Rows)
116 {
117 //临时修改,请注意导入后用代码按要求修改
118 row[sqlTableCloumnName[i].ToString()] = "需要修改列";
119 }
120 }
121 else
122 {
123 foreach (DataRow row in ds.Tables[0].Rows)
124 {
125 row[sqlTableCloumnName[i].ToString()] = a.ToString();
126 a++;
127 }
128 }
129 }
130 #endregion
131
132
133 #region
134 //判断用户提交的是否是空表
135 if (ds.Tables[0].Rows.Count == 0)
136 {
137 ds.Clear();
138 conn.Close();
139 string message = "你提交的数据表为空表,请检查后再确认导入!";
140 return message;
141 }
142 #endregion
143
144
145 //关闭打开的Excel文件
146 conn.Close();
147
148
149 //如果目标表不存在则创建
150 string strSql = string.Format("if object_id('{0}') is null create table {0}(", sheetName);
151 foreach (System.Data.DataColumn c in ds.Tables[0].Columns)
152 {
153 strSql += string.Format("[{0}] varchar(255),", c.ColumnName);
154 }
155 strSql = strSql.Trim(',') + ")";
156
157 using (System.Data.SqlClient.SqlConnection sqlcOnn= new System.Data.SqlClient.SqlConnection(connectionString))
158 {
159 sqlconn.Open();
160 System.Data.SqlClient.SqlCommand command = sqlconn.CreateCommand();
161 command.CommandText = strSql;
162 command.ExecuteNonQuery();
163 sqlconn.Close();
164 }
165
166
167 //用bcp导入数据
168 using (System.Data.SqlClient.SqlBulkCopy bcp = new System.Data.SqlClient.SqlBulkCopy(connectionString))
169 {
170
171 //bcp.SqlRowsCopied += new System.Data.SqlClient.SqlRowsCopiedEventHandler(OnSqlRowsCopied);
172 bcp.BatchSize = 100;//每次传输的行数
173 bcp.NotifyAfter = 100;//进度提示的行数
174 bcp.DestinatiOnTableName= sheetName;//目标表
175 bcp.WriteToServer(ds.Tables[0]);
176 }
177
178 return "数据导入成功!";
179 }
180 catch
181 {
182 return "数据导入失败!";
183 }
184 }
185
186 }
用户页面.cs代码如下:
public void ButtonImport_Click(object sender, EventArgs e)
{
//判断是否选择了上传文件
if (FileUpload.HasFile == false)
{
LblJudgeMseeage.Visible = true;
this.LblJudgeMseeage.Text = "请选择你要导入的Excel文件!";
return;
}
//判断上传的文件是否为Excel文件
string ext_name = Path.GetExtension(FileUpload.FileName).ToString().ToLower();
if (ext_name != ".xls")
{
LblJudgeMseeage.Visible = true;
LblJudgeMseeage.Text = "你选择的文件不是Excel文件!请重新选择!";
return;
}
else
{
string file_name = FileUpload.FileName;//获取上传文件名
//TODO:此处要添加你的Excel文件的存储路径
string file_fullname = Server.MapPath("你的Excel文件的存储路径" + file_name);//含路径全名
FileUpload.PostedFile.SaveAs(file_fullname);//保存文件
//获取连接字
string cOnnStr= ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
//调用公共导入函数
Common.ImportExcel com = new ImportExcel();
string Msg = com.TransferData(file_fullname, "Users", connStr);
LblMessage.Visible = true;
LblMessage.Text = Msg;
//数据导入完成后删除该Excel工作表
File.Delete(file_fullname);
}
}
到此为止,这个Excel文件导入到Sql Server数据库的模块就已经搭建起来了,在这里和大家交流一下编写这个模块的心得体会:
最初在做这个的时候,只是为了把最基本的功能实现,能把数据导入到数据库就行了。可是后来,随着功能一步一步的实现,并且结合自己在测试中的用户体验,我慢慢的觉得,作为一个开发人员,永远都不能只按自己的思路去思考问题。自己做出来的功能模块,对他的操作和使用自己当然是最清楚的,但是你却不能要求客户一定要有和你一样的想法,你应该做的是尽量去满足客户的需求。就以这个导入数据为例,最初在设计导入时,要求客户提供的数据必须和数据表列名字段都完全吻合才可以,不错既然要实现导入就必须得保证数据的完整性,但是那样以来的话,也就意味着用户在导入数据前要在Excel文件中添加一些与他毫不相关的数据。虽然那些数据也是那张数据表中必须的,但对于用户来说根本就毫无意义,而且还会增加用户的负担,所以此时这些工作就自然也是我们所需要考虑的,那么这一实现过程就必须要求开发人员去预料更多的突发情况和可能性,并从各方面去为这些可能性提供解决办法,这样才能使程序功能更加的健壮可靠,才能让用户得到更加人性化的体验!谨以此愚见和大家共勉,若大家有更好的想法和做法,乐意与大家交流共享!