C# Net 使用 openxml 讀取Excel到對象


 

C# Net 使用 openxml 讀取 Excel

C# Net 使用 openxml 讀取Excel到對象

C# Net Core 使用 openxml 讀取Excel

C# Net Core 使用 openxml 讀取Excel到對象

 

注:需要寫入對象到Excel請參考另一篇博客(https://www.cnblogs.com/ping9719/p/12539737.html)

 

------------------------------------------------------------

------------------------------------------------------------

-------------------------文尾看效果---------------------

------------------------------------------------------------

------------------------------------------------------------

 

加入包:OpenXml

創建文件:ExcelRead.cs

復制下面全部代碼到文件 ExcelRead.cs

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Office2010.ExcelAc;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using YGNT.Office.ExcelXml.Models;

namespace YGNT.Office.ExcelXml
{
    /// <summary>
    /// 讀取Excel
    /// </summary>
    public class ExcelRead
    {
        /// <summary>
        /// 讀取、解析
        /// </summary>
        /// <param name="fileName">文件</param>
        /// <param name="sheetName">工作表(默認第一個)</param>
        /// <param name="type">1 不去空格 2 前后空格 3 所有空格  </param>
        /// <returns></returns>
        public static List<ExcelCellInfo> Read(string fileName, string sheetName = "", int type = 2)
        {
            List<ExcelCellInfo> excelCellInfos = new List<ExcelCellInfo>();

            using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
            {
                WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
                //查找工作薄
                Sheet sheet = ExcelSeek.SeekSheet(workbookPart, sheetName);

                //工作表
                WorksheetPart worksheetPart = (WorksheetPart)spreadsheetDocument.WorkbookPart.GetPartById(sheet.Id);
                //數據行
                var rows = worksheetPart.Worksheet.Descendants<Row>();//獲得Excel中得數據行
                foreach (Row r in rows)
                {
                    foreach (Cell c in r.Elements<Cell>())
                    {
                        ExcelCellInfo excelCellInfo = new ExcelCellInfo();
                        excelCellInfo.RowIndex = (int)r.RowIndex.Value;
                        excelCellInfo.CellReference = c.CellReference;
                        excelCellInfo.ColumnIndex = ExcelAlphabet.ABCToColumn(excelCellInfo.CellReference.Replace(excelCellInfo.RowIndex.ToString(), ""));
                        excelCellInfo.Value = GetCellValue(c, workbookPart, type);

                        excelCellInfos.Add(excelCellInfo);
                    }
                }
            }

            return excelCellInfos;
        }

        /// <summary>
        /// 讀取、解析
        /// </summary>
        /// <param name="fileName">文件</param>
        /// <param name="sheetName">工作表(默認第一個)</param>
        /// <param name="type">1 不去空格 2 前后空格 3 所有空格  </param>
        /// <returns></returns>
        public static List<T> Read<T>(string fileName, string sheetName = "", int type = 2) where T : new()
        {
            List<ExcelCellInfo> excelCellInfos = Read(fileName, sheetName, type);

            List<T> t = new List<T>();
            //所有屬性
            var properties = new T().GetType().GetProperties();
            //exc中第一行單元格
            var oneRow = excelCellInfos.Where(o => o.RowIndex == 1 && !string.IsNullOrEmpty(o.Value));
            //屬性和單元格關系(key:屬性,val:單元格)
            var p_OneROw = new Dictionary<System.Reflection.PropertyInfo, ExcelCellInfo>();

            //給【p_OneROw】賦值
            foreach (var property in properties)
            {
                //取屬性上的自定義特性
                ExcelColumnAttribute att = null;
                var atts = (IEnumerable<ExcelColumnAttribute>)property.GetCustomAttributes(typeof(ExcelColumnAttribute), false);
                if (atts.Any())
                    att = atts.First();

                if (att != null && att.IsShow)
                {
                    string eName = att.ColumnName;
                    var lie = oneRow.FirstOrDefault(o => o.Value == eName);
                    if (lie != null)
                    {
                        p_OneROw.Add(property, lie);
                    }
                }
                else
                {
                    string eName = property.Name;
                    var lie = oneRow.FirstOrDefault(o => o.Value == eName);
                    if (lie != null)
                    {
                        p_OneROw.Add(property, lie);
                    }
                }
            }

            for (int i = 2; i <= excelCellInfos.Max(o => o.RowIndex); i++)
            {
                var model = new T();
                foreach (var por in p_OneROw)
                {
                    var clee = excelCellInfos.FirstOrDefault(o => o.RowIndex == i && o.ColumnIndex == por.Value.ColumnIndex);
                    if (clee != null)
                    {
                        string ty = por.Key.PropertyType.FullName;

                        if (ty.Contains("System.String"))
                            por.Key.SetValue(model, clee.Value);
                        else if (ty.Contains("System.DateTime"))
                            por.Key.SetValue(model, Convert.ToDateTime(clee.Value));
                        else if (ty.Contains("System.Single"))
                            por.Key.SetValue(model, Convert.ToSingle(clee.Value));
                        else if (ty.Contains("System.Boolean"))
                            por.Key.SetValue(model, Convert.ToBoolean(clee.Value));
                        else if (ty.Contains("System.Byte"))
                            por.Key.SetValue(model, Convert.ToByte(clee.Value));
                        else if (ty.Contains("System.Int16"))
                            por.Key.SetValue(model, Convert.ToInt16(clee.Value));
                        else if (ty.Contains("System.Int32"))
                            por.Key.SetValue(model, Convert.ToInt32(clee.Value));
                        else if (ty.Contains("System.Int64"))
                            por.Key.SetValue(model, Convert.ToInt64(clee.Value));
                        else if (ty.Contains("System.Double"))
                            por.Key.SetValue(model, Convert.ToDouble(clee.Value));
                        else if (ty.Contains("System.Decimal"))
                            por.Key.SetValue(model, Convert.ToDecimal(clee.Value));
                    }
                }
                t.Add(model);
            }

            return t;
        }

        /// <summary>
        /// 讀取、解析
        /// </summary>
        /// <param name="stream">文件</param>
        /// <param name="sheetName">工作表(默認第一個)</param>
        /// <param name="type">1 不去空格 2 前后空格 3 所有空格  </param>
        /// <returns></returns>
        public static List<ExcelCellInfo> Read(Stream stream, string sheetName = "", int type = 2)
        {
            List<ExcelCellInfo> excelCellInfos = new List<ExcelCellInfo>();

            using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(stream, false))
            {
                WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
                //查找工作薄
                Sheet sheet = ExcelSeek.SeekSheet(workbookPart, sheetName);

                //工作表
                WorksheetPart worksheetPart = (WorksheetPart)spreadsheetDocument.WorkbookPart.GetPartById(sheet.Id);
                //數據行
                var rows = worksheetPart.Worksheet.Descendants<Row>();//獲得Excel中得數據行
                foreach (Row r in rows)
                {
                    foreach (Cell c in r.Elements<Cell>())
                    {
                        ExcelCellInfo excelCellInfo = new ExcelCellInfo();
                        excelCellInfo.RowIndex = (int)r.RowIndex.Value;
                        excelCellInfo.CellReference = c.CellReference;
                        excelCellInfo.ColumnIndex = ExcelAlphabet.ABCToColumn(excelCellInfo.CellReference.Replace(excelCellInfo.RowIndex.ToString(), ""));
                        excelCellInfo.Value = GetCellValue(c, workbookPart, type);

                        excelCellInfos.Add(excelCellInfo);
                    }
                }
            }

            return excelCellInfos;
        }

        /// <summary>
        /// 讀取、解析
        /// </summary>
        /// <param name="stream">文件</param>
        /// <param name="sheetName">工作表(默認第一個)</param>
        /// <param name="type">1 不去空格 2 前后空格 3 所有空格  </param>
        /// <returns></returns>
        public static List<T> Read<T>(Stream stream, string sheetName = "", int type = 2) where T : new()
        {
            List<ExcelCellInfo> excelCellInfos = Read(stream, sheetName, type);

            List<T> t = new List<T>();
            //所有屬性
            var properties = new T().GetType().GetProperties();
            //exc中第一行單元格
            var oneRow = excelCellInfos.Where(o => o.RowIndex == 1 && !string.IsNullOrEmpty(o.Value));
            //屬性和單元格關系(key:屬性,val:單元格)
            var p_OneROw = new Dictionary<System.Reflection.PropertyInfo, ExcelCellInfo>();

            //給【p_OneROw】賦值
            foreach (var property in properties)
            {
                //取屬性上的自定義特性
                ExcelColumnAttribute att = null;
                var atts = (IEnumerable<ExcelColumnAttribute>)property.GetCustomAttributes(typeof(ExcelColumnAttribute), false);
                if (atts.Any())
                    att = atts.First();

                if (att != null && att.IsShow)
                {
                    string eName = att.ColumnName;
                    var lie = oneRow.FirstOrDefault(o => o.Value == eName);
                    if (lie != null)
                    {
                        p_OneROw.Add(property, lie);
                    }
                }
                else
                {
                    string eName = property.Name;
                    var lie = oneRow.FirstOrDefault(o => o.Value == eName);
                    if (lie != null)
                    {
                        p_OneROw.Add(property, lie);
                    }
                }
            }

            for (int i = 2; i <= excelCellInfos.Max(o => o.RowIndex); i++)
            {
                var model = new T();
                foreach (var por in p_OneROw)
                {
                    var clee = excelCellInfos.FirstOrDefault(o => o.RowIndex == i && o.ColumnIndex == por.Value.ColumnIndex);
                    if (clee != null)
                    {
                        por.Key.SetValue(model, clee.Value);
                    }
                }
                t.Add(model);
            }

            return t;
        }

        /// <summary>
        /// 獲取單位格的值
        /// </summary>
        /// <param name="cell">單元格</param>
        /// <param name="workbookPart"></param>
        /// <param name="type">1 不去空格 2 前后空格 3 所有空格  </param>
        /// <returns></returns>
        public static string GetCellValue(Cell cell, WorkbookPart workbookPart, int type = 2)
        {
            //合並單元格不做處理
            if (cell.CellValue == null)
                return string.Empty;

            string cellInnerText = cell.CellValue.InnerXml;

            //純字符串
            if (cell.DataType != null && (cell.DataType.Value == CellValues.SharedString || cell.DataType.Value == CellValues.String || cell.DataType.Value == CellValues.Number))
            {
                //獲取spreadsheetDocument中共享的數據
                SharedStringTable stringTable = workbookPart.SharedStringTablePart.SharedStringTable;

                //如果共享字符串表丟失,則說明出了問題。
                if (!stringTable.Any())
                    return string.Empty;

                string text = stringTable.ElementAt(int.Parse(cellInnerText)).InnerText;
                if (type == 2)
                    return text.Trim();
                else if (type == 3)
                    return text.Replace(" ", "");
                else
                    return text;
            }
            //bool類型
            else if (cell.DataType != null && cell.DataType.Value == CellValues.Boolean)
            {
                return (cellInnerText != "0").ToString().ToUpper();
            }
            //數字格式代碼(numFmtId)小於164是內置的:https://www.it1352.com/736329.html
            else
            {
                //為空為數值
                if (cell.StyleIndex == null)
                    return cellInnerText;

                Stylesheet styleSheet = workbookPart.WorkbookStylesPart.Stylesheet;
                CellFormat cellFormat = (CellFormat)styleSheet.CellFormats.ChildElements[(int)cell.StyleIndex.Value];

                uint formatId = cellFormat.NumberFormatId.Value;
                double doubleTime;//OLE 自動化日期值
                DateTime dateTime;//yyyy/MM/dd HH:mm:ss
                switch (formatId)
                {
                    case 0://常規
                        return cellInnerText;
                    case 9://百分比【0%】
                    case 10://百分比【0.00%】
                    case 11://科學計數【1.00E+02】
                    case 12://分數【1/2】
                        return cellInnerText;
                    case 14:
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("yyyy/MM/dd");
                    //case 15:
                    //case 16:
                    case 17:
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("yyyy/MM");
                    //case 18:
                    //case 19:
                    case 20:
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("H:mm");
                    case 21:
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("HH:mm:ss");
                    case 22:
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("yyyy/MM/dd HH:mm");
                    //case 45:
                    //case 46:
                    case 47:
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("yyyy/MM/dd");
                    case 58://【中國】11月11日
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("MM/dd");
                    case 176://【中國】2020年11月11日
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("yyyy/MM/dd");
                    case 177://【中國】11:22:00
                        doubleTime = double.Parse(cellInnerText);
                        dateTime = DateTime.FromOADate(doubleTime);
                        return dateTime.ToString("HH:mm:ss");
                    default:
                        return cellInnerText;
                }
            }
        }

    }
}

 

創建文件:ExcelCellInfo.cs

復制下面全部代碼到文件 ExcelCellInfo.cs

 

    /// <summary>
    /// 單元格信息
    /// </summary>
    public class ExcelCellInfo
    {
        /// <summary>
        /// 行號,最小1
        /// </summary>
        public int RowIndex { get; set; }
        /// <summary>
        /// 列號,最小1
        /// </summary>
        public int ColumnIndex { get; set; }
        /// <summary>
        /// 單元格地址,如A1
        /// </summary>
        public string CellReference { get; set; }
        /// <summary>
        /// 單元格值
        /// </summary>
        public string Value { get; set; }
    }

  

 

創建文件:ExcelAlphabet.cs

復制下面全部代碼到文件 ExcelAlphabet.cs

 

using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Collections.Generic;
using System.Text;

namespace YGNT.Office.ExcelXml
{
    /// <summary>
    /// Excel字母碼幫助(26進制轉換)
    /// </summary>
    public class ExcelAlphabet
    {
        //備注 A 對應char為65,Z 對應char為90

        /// <summary>
        /// 26個字母
        /// </summary>
        public static uint AlphabetCount = 26;

        /// <summary>
        /// 數字轉字符
        /// </summary>
        /// <param name="iNumber"></param>
        /// <returns></returns>
        public static string ColumnToABC(int iNumber)
        {
            if (iNumber < 1 || iNumber > 702)
                throw new Exception("轉為26進制可用10進制范圍為1-702");

            string sLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            int iUnits = 26;
            int iDivisor = (int)(iNumber / iUnits);
            int iResidue = iNumber % iUnits;
            if (iDivisor == 1 && iResidue == 0)
            {
                iDivisor = 0;
                iResidue = iResidue + iUnits;
            }
            else
            {
                if (iResidue == 0)
                {
                    iDivisor -= 1;
                    iResidue += iUnits;
                }
            }
            if (iDivisor == 0)
            {
                return sLetters.Substring(iResidue - 1, 1);
            }
            else
            {
                return sLetters.Substring(iDivisor - 1, 1) + sLetters.Substring(iResidue - 1, 1);
            }
        }

        /// <summary>
        /// 字符轉數字
        /// </summary>
        /// <param name="sString"></param>
        /// <returns></returns>
        public static int ABCToColumn(string sString)
        {
            if (string.Compare(sString, "A") == -1 || string.Compare(sString, "ZZ") == 1)
                return 0;

            string sLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            int iUnits = 26;
            int sFirst = -1;
            int sSecond = 0;
            if (sString.Length == 1)
            {
                sSecond = sLetters.IndexOf(sString);
            }
            else
            {
                sFirst = sLetters.IndexOf(sString.Substring(0, 1));
                sSecond = sLetters.IndexOf(sString.Substring(1, 1));
            }
            return (sFirst + 1) * iUnits + (sSecond + 1);
        }
    }
}

  

創建文件:ExcelColumnAttribute.cs

復制下面全部代碼到文件 ExcelColumnAttribute.cs

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;

namespace YGNT.Office.ExcelXml
{
    /// <summary>
    /// Excel列特性
    /// </summary>
    public class ExcelColumnAttribute : Attribute
    //: DescriptionAttribute
    {
        /// <summary>
        /// 建議列名
        /// </summary>
        public virtual string ColumnName { get; }

        /// <summary>
        /// 是否顯示列
        /// </summary>
        public virtual bool IsShow { get; }

        /// <summary>
        /// 初始化Excel列名的特性
        /// </summary>
        /// <param name="isShow">是否顯示列(在類上為false時不解析默認第一行,在屬性上為false時不顯示屬性的值)</param>
        public ExcelColumnAttribute(bool isShow = true)
        {
            IsShow = isShow;
        }

        /// <summary>
        /// 初始化Excel列名的特性
        /// </summary>
        /// <param name="description">建議列名(在屬性上為Excel中的第一行的頭值)</param>
        /// <param name="isShow">是否顯示列(在類上為false時不解析默認第一行,在屬性上為false時不顯示屬性的值)</param>
        public ExcelColumnAttribute(string description, bool isShow = true)
        {
            ColumnName = description;
            IsShow = isShow;
        }

    }
}

  

創建文件:ExcelSeek.cs

復制下面全部代碼到文件 ExcelSeek.cs

 

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YGNT.Office.ExcelXml
{
    public class ExcelSeek
    {
        /// <summary>
        /// 在工作薄中查找工作表
        /// </summary>
        public static Sheet SeekSheet(WorkbookPart workbookPart, string sheetName = "")
        {
            //獲取所有工作薄
            IEnumerable<Sheet> sheets = workbookPart.Workbook.Descendants<Sheet>();
            Sheet sheet = null;

            if (!sheets.Any())
                throw new ArgumentException("空的Excel文檔");

            if (string.IsNullOrEmpty(sheetName))
                sheet = sheets.First();
            else
            {
                if (sheets.Count(o => o.Name == sheetName) <= 0)
                    throw new ArgumentException($"沒有找到工作薄“{sheetName}”");
                sheet = sheets.First(o => o.Name == sheetName);
            }
            return sheet;
        }

        /// <summary>
        /// 根據工作表獲取工作頁
        /// </summary>
        /// <param name="sheet">工作表</param>
        /// <returns>工作頁</returns>
        public static WorksheetPart GetWorksheetPart(WorkbookPart workbookPart, Sheet sheet)
        {
            return (WorksheetPart)workbookPart.GetPartById(sheet.Id);
        }

    }
}

  

 

 

--------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------

-------------開始調用(讀取文件信息到對象集合)----------------------------

--------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------

創建一個模型

using System.ComponentModel.DataAnnotations;
using YGNT.Office.ExcelXml;

namespace YGNT.Model.Student
{
    public class StudentExcelDto
    {
        /// <summary>
        /// 班級名稱
        /// </summary>
        [ExcelColumn("班級名稱(必填)")]
        public string ClassName { get; set; }
        /// <summary>
        /// 學員姓名
        /// </summary>
        [ExcelColumn("學員姓名(必填)")]
        public string StudentName { get; set; }
        /// <summary>
        /// 手機號碼
        /// </summary>
        [ExcelColumn("手機號碼")]
        public string Mobile { get; set; }
     //省略其他信息........... } }

  

估計另一篇博文,可以根據模型生成模板Excel文件(給用戶),這里也可以自己准備模板文件

                var path = ExcelCreate.NewCreate();
                ExcelWrite.WriteObj(path, new List<StudentExcelDto>());

  自動生成的模板文件:

 

 

 

在上面的文檔中填入信息。。。

 

 

 

獲取文檔中的信息

var data = ExcelRead.Read<StudentExcelDto>("學員導入模板.xlsx");

  

效果為:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM