项目中发现使用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 OpenXMLHelper
{
/// <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)
{
Class1 cl = new Class1();
cl.Open(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() == )
{
return null;//没有数据
} WorksheetPart workSheetPart = workBookPart.GetPartById(sheets.First().Id) as WorksheetPart;
//获取Excel中得到的行
sheetRows = workSheetPart.Worksheet.Descendants<Row>(); return sheetRows;
} /// <summary>
/// 根据WorkbookPart和sheetName获取该Sheet下所有Row数据
/// </summary>
/// <param name="workBookPart">WorkbookPart对象</param>
/// <param name="sheetName">SheetName</param>
/// <returns>该SheetName下的所有Row数据</returns>
public IEnumerable<Column> GetWorkBookPartColumns(WorkbookPart workBookPart, string sheetName)
{
IEnumerable<Column> sheetColumns = null;
//根据表名在WorkbookPart中获取Sheet集合
IEnumerable<Sheet> sheets = workBookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName);
if (sheets.Count() == )
{
return null;//没有数据
} WorksheetPart workSheetPart = workBookPart.GetPartById(sheets.First().Id) as WorksheetPart;
//获取Excel中得到的行
sheetColumns = workSheetPart.Worksheet.Descendants<Column>(); return sheetColumns;
} /// <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);
IEnumerable<Column> sheetColumns = GetWorkBookPartColumns(workBookPart, sheetName); if (sheetRows == null || sheetRows.Count() <= )
{
return null;
} foreach (Column col in sheetColumns)
{
uint str = col.Min; } //将数据导入DataTable,假定第一行为列名,第二行以后为数据
foreach (Row row in sheetRows)
{
//获取Excel中的列头
if (row.RowIndex == )
{
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 > )
{
string numFmtId = reader.Attributes[].Value;//格式化ID
string formatCode = reader.Attributes[].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 = ;//单元格索引
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 == )//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 == "") ? "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 > && cell.StyleIndex != null)//对于数字,cell.StyleIndex==null
{
int styleIndex = Convert.ToInt32(cell.StyleIndex.Value);
string cellStyle = dicStyles[styleIndex];//获取该索引的样式
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 + );
}
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
cellValue = dateTime.ToString();
}
else//其他的货币、数值
{
cellStyle = cellStyle.Substring(cellStyle.LastIndexOf('.') - ).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) == ).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() <= )
{
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 == )
{
columnValues.Add(cellValue);
}
else
{
rowCellValues.Add(cellValue);
}
}
if (row.RowIndex > )
{
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 = ; 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 - ) % ;
string cellRef = Convert.ToChar( + 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 = ; 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 - ) % ;
string cellRef = Convert.ToChar( + 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() <= )
{
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 == )
{
columnValues.Add(cellValue);
}
else
{
rowCellValues.Add(cellValue);
}
}
if (row.RowIndex > )
{
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 = ; 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外观更加好看

Open XML操作Excel导入数据的更多相关文章

  1. Excel导入数据到Sql server 中出错:“文本被截断,或者一个或多个字符在目标代码页中没有匹配项”

    从Excel导入数据到Sql server 时,由于表中的数据有的很长,导入时出现如下错误(如果数据不是很长,255内以内,则不会出现错误): 出错原因: SQL Server的导入导出为了确定数据表 ...

  2. 使用Open xml 操作Excel系列之二--从data table导出数据到Excel

    由于Excel中提供了透视表PivotTable,许多项目都使用它来作为数据分析报表. 在有些情况下,我们需要在Excel中设计好模板,包括数据源表,透视表等, 当数据导入到数据源表时,自动更新透视表 ...

  3. apache POI 操作excel<导入导出>

    1.首先导入maven依赖 <!-- POI核心依赖 --> <dependency> <groupId>org.apache.poi</groupId> ...

  4. Open xml 操作Excel 透视表(Pivot table)-- 实现Excel多语言报表

    我的一个ERP项目中,客户希望使用Excel Pivot table 做分析报表. ERP 从数据库中读出数据,导出到Excel中的数据源表(统一命名为Data),刷新Pivot table! 客户还 ...

  5. NPOI操作Excel导入DataTable中

    using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using System.Data; using System.IO; using NPOI.X ...

  6. 项目经验之:再来一章:excel导入数据 封装成最棒的不容易!!!

    我见过很的系统,包括OA,ERP,CRM等,在常用的功能当中,从外部导入数据是最常用到的.因为很多客户需要以excel的形式提供数据,,这样的方式我们又如何做呢, 大家最常见的做法可能是这样的,在需要 ...

  7. 操作Excel导入的问题(转)

    当Excel导入成为需要时,之前的导出Excel为html方式的方法就受阻了,于是,需要开始新的百度与google来解决问题. 前提为OLEDB+Excel. 根据需求,多数是对于表的数据的导入.于是 ...

  8. POI操作Excel导入和导出

    Apache的POI组件是Java操作Microsoft Office办公套件的强大API,当中对Word,Excel和PowperPoint都有支持,当然使用较多的还是Excel.由于Word和Po ...

  9. 使用Open xml 操作Excel系列之一-读取Excel

    一. 安装Open Xml SDK 从微软网站下载Open xml SDK,安装SDK. 二. 在项目中添加对DocumentFormat.OpenXml库的引用

随机推荐

  1. 二、jdk命令之javah命令(C Header and Stub File Generator)

    目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...

  2. http://www.bootcss.com/p/font-awesome/

    集成 将Font Awesome 集成到 Bootstrap 非常容易,还可以被单独使用. 最简单的 Bootstrap + Font Awesome 集成方式 使用这种方式将 Font Awesom ...

  3. [ML] CostFunction [Octave code]

    function J = computeCostMulti(X, y, theta) m = length(y); % number of training examples J = 0; for i ...

  4. module.exports 、exports、export、export default的区别

    module.exports和exports是属于 CommonJS 模块规范,export和export default是属于ES6语法. module.exports和exports导出模块,用r ...

  5. vue组件化开发实践

    前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了一下的内容.首先会对使用Vue进行开发的一些前期需要的技术储备进行简单 ...

  6. JDK常用命令

    转自:https://www.cnblogs.com/saiQsai/p/10353044.html 1.jps 查看java进程,得到进程ID:7854 作用等同于:ps -ef | grep ja ...

  7. python的接口

    写法一: class Payment: def pay(self, money): raise NotImplementedError class Alipay(Payment): def pay(s ...

  8. FCCMBBTN.RES

    [ilink32 Error] Error: Unable to open file 'FCCMBBTN.RES' 用到了fc控件 添加路径到LibPath即可. 1st\1stClassStudio ...

  9. GitHub中README.md文件的编辑和使用

    最近对它的README.md文件颇为感兴趣.便写下这贴,帮助更多的还不会编写README文件的同学们. README文件后缀名为md.md是markdown的缩写,markdown是一种编辑博客的语言 ...

  10. ansible debug

    <demoredis_redis_1> ESTABLISH DOCKER CONNECTION FOR USER: ?<demoredis_redis_1> EXEC ['/u ...