C# DotNet 使用 openxml 读取Excel到对象

xingyun86 2022-4-10 1573


C# Net 使用 openxml 读取Excel到对象

 [原文地址:C# Net 使用 openxml 读取Excel到对象 - 爱恋的红尘 - 博客园 (cnblogs.com)]

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);
        }
 
    }
}

创建一个模型

sing 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; }<br>
     //省略其他信息...........
    }
}

估计另一篇博文,可以根据模型生成模板Excel文件(给用户),这里也可以自己准备模板文件

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

自动生成的模板文件:


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


获取文档中的信息

var data = ExcelRead.Read<StudentExcelDto>("学员导入模板.xlsx");

效果为:



×
打赏作者
最新回复 (0)
只看楼主
全部楼主
返回