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");
效果为:


浙公网安备 33010602011771号