Open XML操作Excel導入數據

項目中發現使用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外觀更加好看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章