項目中發現使用OleDb(using System.Data.OleDb)相關對象處理Excel導入功能,不是很穩定經常出問題,需要把這個問題解決掉。項目組提出使用OpenXML來處理Excel的導入、導出問題,出於興趣對OpenXML瞭解一下,做了簡單Demo。
1.Open XML準備
使用Open XML操作Excel需要安裝Open XML Format SDK 2.0及其以上版本,其相關對象和接口建立在Open XML SDK CTP 2基礎上的,使用前先下載Open
XML Format SDK 2.0及其以上版本。SDK默認會安裝在C:\Program Files (x86)\Open XML Format SDK\V2.0 (64bit)目錄下,lib子目錄下的DocumentFormat.OpenXml.dll必須被引用到項目中。Open XML支持Office 2007及其以上版本,Open XML好像升級到2.5版本了,對於Open XML 2.0和2.5其對象和API接口有所不同,請查閱相關文檔。把這個小Demo整出來,花了一些時間,主要是對其中的相關對象和API接口使用的不瞭解。
2.簡單Excel zip包介紹
大家應該知道Office 2007都是一些XML文件的壓縮包,可以創建一個Office 2007的Excel文件,簡單錄入幾條數據,保存一下。複製一下,做個副本,修改其後綴爲zip格式,這樣就可以看到Excel的一些相關文件。因需要測試功能,做了簡單的Office 2007的文件,修改爲zip解壓查看相關文件如下圖:
其中需要注意的幾個文件styles.xml、sharedStrings.xml、workbook.xml、worksheets中各個sheet。
styles.xml:主要用來存放Excel中樣式的(包括格式化的數據格式,如日期、貨幣等等);
sharedStrings.xml:主要存放共享的String數據的,在沒有對Excel中相關單元格和數據格式化都可以通過這個文件讀取;
workbook.xml:主要存放工作簿中各個工作表的命名和命名空間,在獲取各個工作表的名稱可以通過尋址找到節點,獲取各表名稱;
worksheets中各個sheet:主要存放各個工作表的相關數據庫 可以通過下面這個圖瞭解各個對象的關係,這個是Open
XML 2.5開發的相關對象(http://msdn.microsoft.com/zh-cn/library/office/gg278316.aspx):
3.簡單功能介紹
使用OpenXML操作Excel,將Excel中的數據正確讀取出來,保持到數據庫。但是碰到一些問題,就是如何讀取格式化的數據,把日期、時間、貨幣進行數據格式化,就不能正確的讀取數據,由於不是很瞭解,花了些時間,在網上查了查相關資料解決了一下。估計不是最優解,如果對這方面瞭解的大牛,希望能指導一下,提供一些更好的方法。
這裏測試了兩塊,一塊把Excel的所有數據按Excel定義的格式轉換成DataTable和DataSet;另一塊把Excel中數據對照相關數據庫實體對象,使用反射進行實體屬性賦值,轉換失敗,則Excel中的數據就有問題。
一塊是將Excel的數據全部搬到DataTable或DataSet,不考慮這些數據是來自數據庫的幾個表,如果業務需要可以對這個DataTable或DataSet操作;
另一塊是進行數據庫實體對象校驗,必須Excel中單元格的數據格式和數據庫中字段的存儲格式一致,當然也可以根據業務的需要繼續添加各種驗證,你可以繼續豐富、優化代碼。
稍加改進了一下,可以支持泛型對象的轉換,可以將符合規格的Excel的數據轉換成對應的實體對象,即可以映射任何實體對象。那麼就可以根據需要轉換成DataTable或DataSet,或者對多表數據的轉換,可以在此基礎上優化,希望對你有幫助。
4.簡單實現介紹
實現就是建一個WinForm程序,一個導入按鈕,一個DataView呈現數據,一個OpenFileDialog選擇文件。兩個輔助解析Excel的類,ExcelOper和ExcelOperMatch,一個是不進行校驗之間轉化爲DataTable\DataSet的;一個是需要數據校驗的,其中ExcelOperMatch調用ExcelOper寫好的兩個方法:GetWorkBookPartRows(獲取WorkBookPart中所有的行數據)和GetCellValue(獲取單元格的值)。其中對於格式化樣式不太好處理,測試數據2的樣式:
5.簡單實現效果
1).測試數據1
2).實現數據1
3).測試數據2
4).實現數據2
5).測試數據3
6).實現數據3
6.示例Demo代碼:
1).ExcelOper.cs文件代碼
(注意DLL和命名空間的引入:using DocumentFormat.OpenXml.Packaging和using DocumentFormat.OpenXml.Spreadsheet和using System.Diagnostics;)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.IO; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; using System.Xml; using System.Diagnostics; using DocumentFormat.OpenXml; using System.Reflection; namespace OpenXMLTest { /// <summary> /// 思考問題: /// 1.對於Excel中所有不進行任何驗證,直接轉化爲Table(有列頭和無列頭) /// 2.對於Excel中數據匹配某一指定表的列頭及其數據(有列頭) /// 3.對於Excel中數據不是處理在一張表中(有列頭和無列頭) /// 4.對於Excel中數據多表處理和單表處理 /// 5.對於Excel中一個Sheet的數據來自多張數據庫表 /// </summary> public class ExcelOper { /// <summary> /// 將DataTable轉化爲XML輸出 /// </summary> /// <param name="dataTable">DataTable</param> /// <param name="fileName">文件名稱</param> public void DataTableToXML(DataTable dataTable, string fileName) { //指定程序安裝目錄 string filePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + fileName; using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write)) { using (XmlWriter xmlWriter = XmlWriter.Create(fs)) { dataTable.WriteXml(xmlWriter, XmlWriteMode.IgnoreSchema); } } Process.Start(filePath); } /// <summary> /// 將Excel多單一表轉化爲DataSet數據集對象 /// </summary> /// <param name="filePath">Excel文件路徑</param> /// <returns>轉化的數據集</returns> public DataSet ExcelToDataSet(string filePath) { DataSet dataSet = new DataSet(); try { using (SpreadsheetDocument spreadDocument = SpreadsheetDocument.Open(filePath, false)) { //指定WorkbookPart對象 WorkbookPart workBookPart = spreadDocument.WorkbookPart; //獲取Excel中SheetName集合 List<string> sheetNames = GetSheetNames(workBookPart); foreach (string sheetName in sheetNames) { DataTable dataTable = WorkSheetToTable(workBookPart, sheetName); if (dataTable != null) { dataSet.Tables.Add(dataTable);//將表添加到數據集 } } } } catch (Exception exp) { //throw new Exception("可能Excel正在打開中,請關閉重新操作!"); } return dataSet; } /// <summary> /// 將Excel單一表轉化爲DataTable對象 /// </summary> /// <param name="sheetName">SheetName</param> /// <param name="stream">Excel文件路徑</param> /// <returns>DataTable對象</returns> public DataTable ExcelToDataTable(string sheetName, string filePath) { DataTable dataTable = new DataTable(); try { //根據Excel流轉換爲spreadDocument對象 using (SpreadsheetDocument spreadDocument = SpreadsheetDocument.Open(filePath, false))//Excel文檔包 { //Workbook workBook = spreadDocument.WorkbookPart.Workbook;//主文檔部件的根元素 //Sheets sheeets = workBook.Sheets;//塊級結構(如工作表、文件版本等)的容器 WorkbookPart workBookPart = spreadDocument.WorkbookPart; //獲取Excel中SheetName集合 List<string> sheetNames = GetSheetNames(workBookPart); if (sheetNames.Contains(sheetName)) { //根據WorkSheet轉化爲Table dataTable = WorkSheetToTable(workBookPart, sheetName); } } } catch (Exception exp) { //throw new Exception("可能Excel正在打開中,請關閉重新操作!"); } return dataTable; } /// <summary> /// 根據WorkbookPart獲取所有SheetName /// </summary> /// <param name="workBookPart"></param> /// <returns>SheetName集合</returns> private List<string> GetSheetNames(WorkbookPart workBookPart) { List<string> sheetNames = new List<string>(); Sheets sheets = workBookPart.Workbook.Sheets; foreach (Sheet sheet in sheets) { string sheetName = sheet.Name; if (!string.IsNullOrEmpty(sheetName)) { sheetNames.Add(sheetName); } } return sheetNames; } /// <summary> /// 根據WorkbookPart和sheetName獲取該Sheet下所有Row數據 /// </summary> /// <param name="workBookPart">WorkbookPart對象</param> /// <param name="sheetName">SheetName</param> /// <returns>該SheetName下的所有Row數據</returns> public IEnumerable<Row> GetWorkBookPartRows(WorkbookPart workBookPart, string sheetName) { IEnumerable<Row> sheetRows = null; //根據表名在WorkbookPart中獲取Sheet集合 IEnumerable<Sheet> sheets = workBookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName); if (sheets.Count() == 0) { return null;//沒有數據 } WorksheetPart workSheetPart = workBookPart.GetPartById(sheets.First().Id) as WorksheetPart; //獲取Excel中得到的行 sheetRows = workSheetPart.Worksheet.Descendants<Row>(); return sheetRows; } /// <summary> /// 根據WorkbookPart和表名創建DataTable對象 /// </summary> /// <param name="workBookPart">WorkbookPart對象</param> /// <param name="tableName">表名</param> /// <returns>轉化後的DataTable</returns> private DataTable WorkSheetToTable(WorkbookPart workBookPart, string sheetName) { //創建Table DataTable dataTable = new DataTable(sheetName); //根據WorkbookPart和sheetName獲取該Sheet下所有行數據 IEnumerable<Row> sheetRows = GetWorkBookPartRows(workBookPart, sheetName); if (sheetRows == null || sheetRows.Count() <= 0) { return null; } //將數據導入DataTable,假定第一行爲列名,第二行以後爲數據 foreach (Row row in sheetRows) { //獲取Excel中的列頭 if (row.RowIndex == 1) { List<DataColumn> listCols = GetDataColumn(row, workBookPart); dataTable.Columns.AddRange(listCols.ToArray()); } else { //Excel第二行同時爲DataTable的第一行數據 DataRow dataRow = GetDataRow(row, dataTable, workBookPart); if (dataRow != null) { dataTable.Rows.Add(dataRow); } } } return dataTable; } /// <summary> /// 根據WorkbookPart獲取NumberingFormats樣式集合 /// </summary> /// <param name="workBookPart">WorkbookPart對象</param> /// <returns>NumberingFormats樣式集合</returns> private List<string> GetNumberFormatsStyle(WorkbookPart workBookPart) { List<string> dicStyle = new List<string>(); Stylesheet styleSheet = workBookPart.WorkbookStylesPart.Stylesheet; OpenXmlElementList list = styleSheet.NumberingFormats.ChildElements;//獲取NumberingFormats樣式集合 foreach (var element in list)//格式化節點 { if (element.HasAttributes) { using (OpenXmlReader reader = OpenXmlReader.Create(element)) { if (reader.Read()) { if (reader.Attributes.Count > 0) { string numFmtId = reader.Attributes[0].Value;//格式化ID string formatCode = reader.Attributes[1].Value;//格式化Code dicStyle.Add(formatCode);//將格式化Code寫入List集合 } } } } } return dicStyle; } /// <summary> /// 根據行對象和WorkbookPart對象獲取DataColumn集合 /// </summary> /// <param name="row">Excel中行記錄</param> /// <param name="workBookPart">WorkbookPart對象</param> /// <returns>返回DataColumn對象集合</returns> private List<DataColumn> GetDataColumn(Row row, WorkbookPart workBookPart) { List<DataColumn> listCols = new List<DataColumn>(); foreach (Cell cell in row) { string cellValue = GetCellValue(cell, workBookPart); DataColumn col = new DataColumn(cellValue); listCols.Add(col); } return listCols; } /// <summary> /// 根據Excel行\數據庫表\WorkbookPart對象獲取數據DataRow /// </summary> /// <param name="row">Excel中行對象</param> /// <param name="dateTable">數據表</param> /// <param name="workBookPart">WorkbookPart對象</param> /// <returns>返回一條數據記錄</returns> private DataRow GetDataRow(Row row, DataTable dateTable, WorkbookPart workBookPart) { //讀取Excel中數據,一一讀取單元格,若整行爲空則忽視該行 DataRow dataRow = dateTable.NewRow(); IEnumerable<Cell> cells = row.Elements<Cell>(); int cellIndex = 0;//單元格索引 int nullCellCount = cellIndex;//空行索引 foreach (Cell cell in row) { string cellVlue = GetCellValue(cell, workBookPart); if (string.IsNullOrEmpty(cellVlue)) { nullCellCount++; } dataRow[cellIndex] = cellVlue; cellIndex++; } if (nullCellCount == cellIndex)//剔除空行 { dataRow = null;//一行中單元格索引和空行索引一樣 } return dataRow; } /// <summary> /// 根據Excel單元格和WorkbookPart對象獲取單元格的值 /// </summary> /// <param name="cell">Excel單元格對象</param> /// <param name="workBookPart">Excel WorkbookPart對象</param> /// <returns>單元格的值</returns> public string GetCellValue(Cell cell, WorkbookPart workBookPart) { string cellValue = string.Empty; if (cell.ChildElements.Count == 0)//Cell節點下沒有子節點 { return cellValue; } string cellRefId = cell.CellReference.InnerText;//獲取引用相對位置 string cellInnerText = cell.CellValue.InnerText;//獲取Cell的InnerText cellValue = cellInnerText;//指定默認值(其實用來處理Excel中的數字) //獲取WorkbookPart中NumberingFormats樣式集合 List<string> dicStyles = GetNumberFormatsStyle(workBookPart); //獲取WorkbookPart中共享String數據 SharedStringTable sharedTable = workBookPart.SharedStringTablePart.SharedStringTable; try { EnumValue<CellValues> cellType = cell.DataType;//獲取Cell數據類型 if (cellType != null)//Excel對象數據 { switch (cellType.Value) { case CellValues.SharedString://字符串 //獲取該Cell的所在的索引 int cellIndex = int.Parse(cellInnerText); cellValue = sharedTable.ChildElements[cellIndex].InnerText; break; case CellValues.Boolean://布爾 cellValue = (cellInnerText == "1") ? "TRUE" : "FALSE"; break; case CellValues.Date://日期 cellValue = Convert.ToDateTime(cellInnerText).ToString(); break; case CellValues.Number://數字 cellValue = Convert.ToDecimal(cellInnerText).ToString(); break; default: cellValue = cellInnerText; break; } } else//格式化數據 { if (dicStyles.Count > 0 && cell.StyleIndex != null)//對於數字,cell.StyleIndex==null { int styleIndex = Convert.ToInt32(cell.StyleIndex.Value); string cellStyle = dicStyles[styleIndex - 1];//獲取該索引的樣式 if (cellStyle.Contains("yyyy") || cellStyle.Contains("h") || cellStyle.Contains("dd") || cellStyle.Contains("ss")) { //如果爲日期或時間進行格式處理,去掉“;@” cellStyle = cellStyle.Replace(";@", ""); while (cellStyle.Contains("[") && cellStyle.Contains("]")) { int otherStart = cellStyle.IndexOf('['); int otherEnd = cellStyle.IndexOf("]"); cellStyle = cellStyle.Remove(otherStart, otherEnd - otherStart + 1); } double doubleDateTime = double.Parse(cellInnerText); DateTime dateTime = DateTime.FromOADate(doubleDateTime);//將Double日期數字轉爲日期格式 if (cellStyle.Contains("m")) { cellStyle = cellStyle.Replace("m", "M"); } if (cellStyle.Contains("AM/PM")) { cellStyle = cellStyle.Replace("AM/PM", ""); } cellValue = dateTime.ToString(cellStyle);//不知道爲什麼Excel 2007中格式日期爲yyyy/m/d } else//其他的貨幣、數值 { cellStyle = cellStyle.Substring(cellStyle.LastIndexOf('.') - 1).Replace("\\", ""); decimal decimalNum = decimal.Parse(cellInnerText); cellValue = decimal.Parse(decimalNum.ToString(cellStyle)).ToString(); } } } } catch (Exception exp) { //string expMessage = string.Format("Excel中{0}位置數據有誤,請確認填寫正確!", cellRefId); //throw new Exception(expMessage); cellValue = "N/A"; } return cellValue; } /// <summary> /// 獲取Excel中多表的表名 /// </summary> /// <param name="filePath"></param> /// <returns></returns> private List<string> GetExcelSheetNames(string filePath) { string sheetName = string.Empty; List<string> sheetNames = new List<string>();//所有Sheet表名 using (SpreadsheetDocument spreadDocument = SpreadsheetDocument.Open(filePath, false)) { WorkbookPart workBook = spreadDocument.WorkbookPart; Stream stream = workBook.GetStream(FileMode.Open); XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load(stream); XmlNamespaceManager xmlNSManager = new XmlNamespaceManager(xmlDocument.NameTable); xmlNSManager.AddNamespace("default", xmlDocument.DocumentElement.NamespaceURI); XmlNodeList nodeList = xmlDocument.SelectNodes("//default:sheets/default:sheet", xmlNSManager); foreach (XmlNode node in nodeList) { sheetName = node.Attributes["name"].Value; sheetNames.Add(sheetName); } } return sheetNames; } #region SaveCell private void InsertTextCellValue(Worksheet worksheet, string column, uint row, string value) { Cell cell = ReturnCell(worksheet, column, row); CellValue v = new CellValue(); v.Text = value; cell.AppendChild(v); cell.DataType = new EnumValue<CellValues>(CellValues.String); worksheet.Save(); } private void InsertNumberCellValue(Worksheet worksheet, string column, uint row, string value) { Cell cell = ReturnCell(worksheet, column, row); CellValue v = new CellValue(); v.Text = value; cell.AppendChild(v); cell.DataType = new EnumValue<CellValues>(CellValues.Number); worksheet.Save(); } private static Cell ReturnCell(Worksheet worksheet, string columnName, uint row) { Row targetRow = ReturnRow(worksheet, row); if (targetRow == null) return null; return targetRow.Elements<Cell>().Where(c => string.Compare(c.CellReference.Value, columnName + row, true) == 0).First(); } private static Row ReturnRow(Worksheet worksheet, uint row) { return worksheet.GetFirstChild<SheetData>(). Elements<Row>().Where(r => r.RowIndex == row).First(); } #endregion } }
2).ExcelOperMatch.cs代碼
(注意命名空間的引入:using System.Reflection;[稍作改進添加反射實體泛型支持方法,這樣就可以將符合規則的Excel數據轉換成對應的數據表])
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; using System.Reflection; using System.Globalization; namespace OpenXMLTest { public class ExcelOperMatch { /// <summary> /// 根據SheetName和文件路徑轉換實體對象 /// 將Excel中的數據映射實體對象集合 /// </summary> /// <param name="sheetName">操作SheetName</param> /// <param name="filePath">文件路徑</param> /// <returns>實體對象集合</returns> public List<TestPerson> ExcelToPersons(string sheetName, string filePath) { List<TestPerson> listTestPersons = new List<TestPerson>(); try { using (SpreadsheetDocument spreadDocument = SpreadsheetDocument.Open(filePath, false))//Excel文檔包 { WorkbookPart workBookPart = spreadDocument.WorkbookPart; listTestPersons = ExtcelToObjects(workBookPart, sheetName); } } catch(Exception exp) { // throw new Exception("可能Excel正在打開中,請關閉重新操作!"); } return listTestPersons; } /// <summary> /// 根據WorkbookPart和SheetName獲取實體對象集合 /// </summary> /// <param name="workBookPart">WorkbookPart對象</param> /// <param name="sheetName">sheetName</param> /// <returns>實體對象集合</returns> private List<TestPerson> ExtcelToObjects(WorkbookPart workBookPart, string sheetName) { List<TestPerson> listPersons = new List<TestPerson>(); List<string> columnValues = new List<string>();//列頭值集合 List<string> rowCellValues = null;//行數據集合 //獲取WorkbookPart下名爲sheetName的Sheet的所有行數據 IEnumerable<Row> sheetRows = new ExcelOper().GetWorkBookPartRows(workBookPart, sheetName); if (sheetRows == null || sheetRows.Count() <= 0) { return null; } //將數據導入DataTable,假定第一行爲列名,第二行以後爲數據 foreach (Row row in sheetRows) { rowCellValues = new List<string>();//新行數據 foreach (Cell cell in row) { //獲取單元格的值 string cellValue = new ExcelOper().GetCellValue(cell, workBookPart); if (row.RowIndex == 1) { columnValues.Add(cellValue); } else { rowCellValues.Add(cellValue); } } if (row.RowIndex > 1) { int rowIndex = Convert.ToInt32(row.RowIndex.ToString()); //使用強類型處理 TestPerson singlePerson = ConvertToTestPerson(rowIndex, columnValues, rowCellValues); //使用泛型處理,可以轉換任意實體 //TestPerson singlePerson = ConvertToObject<TestPerson>(rowIndex, columnValues, rowCellValues); listPersons.Add(singlePerson); } } return listPersons; } /// <summary> /// 根據行號\列集合\行集合轉換實體對象 /// </summary> /// <param name="rowIndex">行索引</param> /// <param name="columnValues">列頭集合</param> /// <param name="rowCellValues">一行數據集合</param> /// <returns>映射的實體對象</returns> private TestPerson ConvertToTestPerson(int rowIndex, List<string> columnValues, List<string> rowCellValues) { TestPerson singlePerson = new TestPerson();//TestPerson對象 foreach (PropertyInfo pi in singlePerson.GetType().GetProperties()) { for (int index = 0; index < columnValues.Count; index++) { try { if (pi.Name.Equals(columnValues[index], StringComparison.OrdinalIgnoreCase)) { String propertyType = pi.PropertyType.Name; switch (propertyType) { case "Int32": pi.SetValue(singlePerson, int.Parse(rowCellValues[index]), null); break; case "DateTime": pi.SetValue(singlePerson, DateTime.Parse(rowCellValues[index]), null); break; case "Decimal": pi.SetValue(singlePerson, Decimal.Parse(rowCellValues[index]), null); break; case "Double": pi.SetValue(singlePerson, Double.Parse(rowCellValues[index]), null); break; case "String": pi.SetValue(singlePerson, rowCellValues[index], null); break; case "Boolean": pi.SetValue(singlePerson, Boolean.Parse(rowCellValues[index]), null); break; } break; } } catch (Exception exp) { index = (index - 1) % 26; string cellRef = Convert.ToChar(65 + index).ToString() +rowIndex; string expMessage = string.Format("請確認Excel中{0}位置數據填寫正確!", cellRef); throw new Exception(expMessage); } } } return singlePerson; } /// <summary> /// 使用泛型,這樣可以針對任何實體對象進行映射參照 /// </summary> /// <typeparam name="T">映射實體對象</typeparam> /// <param name="rowIndex">行號</param> /// <param name="columnValues">列集合</param> /// <param name="rowCellValues">行單元格集合</param> /// <returns>實體對象</returns> private T ConvertToObject<T>(int rowIndex, List<string> columnValues, List<string> rowCellValues) { T singleT = Activator.CreateInstance<T>();//創建實體對象T foreach (PropertyInfo pi in singleT.GetType().GetProperties()) { for (int index = 0; index < columnValues.Count; index++) { try { if (pi.Name.Equals(columnValues[index], StringComparison.OrdinalIgnoreCase)) { String propertyType = pi.PropertyType.Name; switch (propertyType) { case "Int32": pi.SetValue(singleT, int.Parse(rowCellValues[index]), null); break; case "DateTime": pi.SetValue(singleT, DateTime.Parse(rowCellValues[index]), null); break; case "Decimal": pi.SetValue(singleT, Decimal.Parse(rowCellValues[index]), null); break; case "Double": pi.SetValue(singleT, Double.Parse(rowCellValues[index]), null); break; case "String": pi.SetValue(singleT, rowCellValues[index], null); break; case "Boolean": pi.SetValue(singleT, Boolean.Parse(rowCellValues[index]), null); break; } break; } } catch (Exception exp) { index = (index - 1) % 26; string cellRef = Convert.ToChar(65 + index).ToString() + rowIndex; string expMessage = string.Format("請確認Excel中{0}位置數據填寫正確!", cellRef); throw new Exception(expMessage); //singleT = default(T); } } } return singleT; } } /// <summary> /// 輔助測試類 /// </summary> public class TestPerson { #region 公共屬性 //int類型測試 public int PersonID { get; set; } //bool類型測試 public bool PersonSex { get; set; } //string類型測試 public string PersonName { get; set; } //DateTime 日期測試 public DateTime PersonBirth { get; set; } //DateTime 時間測試 public DateTime PersonLogTime { get; set; } //decimal類型測試 public decimal PersonAmountMoney { get; set; } #endregion } }
3).WinForm的Button事件代碼
private void btnOperExcel_Click(object sender, EventArgs e) { openFileDialog1.Filter = "Text Documents (*.xlsx)|*.xlsx|All Files|*.*"; if (openFileDialog1.ShowDialog() == DialogResult.OK) { string filePath = openFileDialog1.FileName; ExcelOper excelOperation = new ExcelOper(); //DataTable dataTable = excelOperation.ExcelToDataTable("Sheet1", filePath); //excelOperation.DataTableToXML(dataTable,"Excel.xml"); //this.dataGridView1.DataSource = dataTable; //DataSet dataSet = excelOperation.ExcelToDataSet(filePath); //this.dataGridView1.DataSource=dataSet.Tables[0]; //ExcelOperMatch excelMatch = new ExcelOperMatch(); //List<TestPerson> listTestPersons = excelMatch.ExcelToPersons("Sheet1", filePath); //this.dataGridView1.DataSource = listTestPersons; } }
4).ExcelOperMatchObject.cs([新增泛型處理Excel的數據,這樣可以輕鬆將Excel的數據轉換成數據庫表])
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; using System.Reflection; using System.Globalization; namespace OpenXMLTest { public class ExcelOperMatchObject<T> { /// <summary> /// 根據SheetName和文件路徑轉換實體對象 /// 將Excel中的數據映射實體對象集合 /// </summary> /// <param name="sheetName">操作SheetName</param> /// <param name="filePath">文件路徑</param> /// <returns>實體對象集合</returns> public List<T> ExcelToObjects(string sheetName, string filePath) { List<T> listTestPersons = new List<T>(); try { using (SpreadsheetDocument spreadDocument = SpreadsheetDocument.Open(filePath, false))//Excel文檔包 { WorkbookPart workBookPart = spreadDocument.WorkbookPart; listTestPersons = ExtcelToObjects(workBookPart, sheetName); } } catch (Exception exp) { // throw new Exception("可能Excel正在打開中,請關閉重新操作!"); } return listTestPersons; } /// <summary> /// 根據WorkbookPart和SheetName獲取實體對象集合 /// </summary> /// <param name="workBookPart">WorkbookPart對象</param> /// <param name="sheetName">sheetName</param> /// <returns>實體對象集合</returns> private List<T> ExtcelToObjects(WorkbookPart workBookPart, string sheetName) { List<T> listPersons = new List<T>(); List<string> columnValues = new List<string>();//列頭值集合 List<string> rowCellValues = null;//行數據集合 //獲取WorkbookPart下名爲sheetName的Sheet的所有行數據 IEnumerable<Row> sheetRows = new ExcelOper().GetWorkBookPartRows(workBookPart, sheetName); if (sheetRows == null || sheetRows.Count() <= 0) { return null; } //將數據導入DataTable,假定第一行爲列名,第二行以後爲數據 foreach (Row row in sheetRows) { rowCellValues = new List<string>();//新行數據 foreach (Cell cell in row) { //獲取單元格的值 string cellValue = new ExcelOper().GetCellValue(cell, workBookPart); if (row.RowIndex == 1) { columnValues.Add(cellValue); } else { rowCellValues.Add(cellValue); } } if (row.RowIndex > 1) { int rowIndex = Convert.ToInt32(row.RowIndex.ToString()); T singlePerson = ConvertToObject<T>(rowIndex, columnValues, rowCellValues); listPersons.Add(singlePerson); } } return listPersons; } /// <summary> /// 根據行號\列頭集合\行數據集合,轉換數據實體對象 /// </summary> /// <typeparam name="T">映射實體對象</typeparam> /// <param name="rowIndex">行號</param> /// <param name="columnValues">列頭集合</param> /// <param name="rowCellValues">行數據集合</param> /// <returns>實體對象</returns> private T ConvertToObject<T>(int rowIndex, List<string> columnValues, List<string> rowCellValues) { //T singlePerson = default(T); T singlePerson = Activator.CreateInstance<T>(); //Type singlePerson = typeof(T); foreach (PropertyInfo pi in singlePerson.GetType().GetProperties()) { for (int index = 0; index < columnValues.Count; index++) { if (pi.Name.Equals(columnValues[index], StringComparison.OrdinalIgnoreCase)) { String propertyType = pi.PropertyType.Name; switch (propertyType) { case "Int32": pi.SetValue(singlePerson, int.Parse(rowCellValues[index]), null); break; case "DateTime": pi.SetValue(singlePerson, DateTime.Parse(rowCellValues[index]), null); break; case "Decimal": pi.SetValue(singlePerson, Decimal.Parse(rowCellValues[index]), null); break; case "Double": pi.SetValue(singlePerson, Double.Parse(rowCellValues[index]), null); break; case "String": pi.SetValue(singlePerson, rowCellValues[index], null); break; case "Boolean": pi.SetValue(singlePerson, Boolean.Parse(rowCellValues[index]), null); break; } break; } } } return singlePerson; } } }
5).對於第4點ExcelOperMatchObject類的調用很簡單(還是在WinForm的Button事件中調用):
//Excel數據導入,泛型支持與任意實體對象匹配 //這裏使用TestPerson實體對象做測試對象,你也可以用多個對象,轉換成DataTable或DataSet ExcelOperMatchObject<TestPerson> execelOperObject = new ExcelOperMatchObject<TestPerson>(); List<TestPerson> listTestPersons = execelOperObject.ExcelToObjects("Sheet1", filePath); this.dataGridView1.DataSource = listTestPersons;
7.代碼結構
1).ExcelOper.cs文件代碼
2).ExcelOperMatch.cs代碼
8.參考博客
《用Open
XML SDK讀取Excel》
《Walkthrough:
Word 2007 XML 格式》
《使用OpenXML將Excel內容讀取到DataTable中》
《SpreadsheetML
文檔的結構 (Open XML SDK)》
《使用
Open XML SDK 2.0 檢索 Excel 2010 中單元格的值》
至於性能方面項目組老大測試過,相對不錯,你也可以用幾十萬條數據試一下。
對於數據庫數據導出Excel的處理感興趣的可以看看:《[轉載]DataSet導出Excel,比以往的方法導出的Excel外觀更加好看》