說起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; i < exl_TableCloumn.Length; i++)
76 {
77 tableInfo = tableInfo + sqlTableCloumnName[i].ToString() + ",";
78
79 }
80 j = sqlTableCloumnName.Length - exl_TableCloumn.Length;
81 //檢查Excel表中的字段順序是否與數據庫中匹配
82 for (i = 0; i < exl_TableCloumn.Length; i++)
83 {
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; p < j; p++, i++)
110 {
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文件中添加一些與他毫不相關的數據。雖然那些數據也是那張數據表中必須的,但對於用戶來說根本就毫無意義,而且還會增加用戶的負擔,所以此時這些工作就自然也是我們所需要考慮的,那么這一實現過程就必須要求開發人員去預料更多的突發情況和可能性,並從各方面去為這些可能性提供解決辦法,這樣才能使程序功能更加的健壯可靠,才能讓用戶得到更加人性化的體驗!謹以此愚見和大家共勉,若大家有更好的想法和做法,樂意與大家交流共享!