现在的项目基本都会使用一些主流的orm框架,几乎不会再用到datable
所以个人使用npoi、泛型、反射写了一个适用于主流orm框架的excel导入、导出帮助类,可以根据实体自动反射出需要导入导出的列名,导入excel时直接生成List<T>
可使用ef、dapper等orm框架直接批量插入List<T>
使用ado.net 的话个人还是觉得写datatable做批量导入好一点
第一步:nuget包安装npoi
第二步:新建ExcelHelper类:
然后直接上代码:
using NPOI.HSSF.UserModel;
using NPOI.HSSF.Util;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using R.Common.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web;
namespace R.Common.Excel
{
public class ExcelHelper<T> where T : new()
{
/// <summary>
/// List<T>导出到Excel的MemoryStream
/// </summary>
/// <param name="list">导出数据</param>
/// <param name="titleText">表头文本</param>
/// <param name="columns">需要导出的属性</param>
/// <param name="columns">设置某些列的宽度</param>
public static MemoryStream ExportToExcel(List<T> list, string titleText, string[] columns, Dictionary<int, int> colWidthDic)
{
HSSFWorkbook workbook = new HSSFWorkbook();
ISheet sheet = workbook.CreateSheet();
try
{
Type type = typeof(T);
PropertyInfo[] properties = ReflexHelper.GetProperties(type, columns);
ICellStyle dateStyle = workbook.CreateCellStyle();
dateStyle.BorderBottom = BorderStyle.Thin;
dateStyle.BorderLeft = BorderStyle.Thin;
dateStyle.BorderRight = BorderStyle.Thin;
dateStyle.BorderTop = BorderStyle.Thin;
dateStyle.BottomBorderColor = HSSFColor.Black.Index;
dateStyle.LeftBorderColor = HSSFColor.Black.Index;
dateStyle.RightBorderColor = HSSFColor.Black.Index;
dateStyle.TopBorderColor = HSSFColor.Black.Index;
dateStyle.VerticalAlignment = VerticalAlignment.Top;
IDataFormat format = workbook.CreateDataFormat();
dateStyle.DataFormat = format.GetFormat("yyyy-MM-dd");
dateStyle.Alignment = HorizontalAlignment.CenterSelection;
#region 获取每列的列宽(最大宽度)
int[] arrColWidth = new int[properties.Length];
for (int columnIndex = 0; columnIndex < properties.Length; columnIndex++)
{
arrColWidth[columnIndex] = properties[columnIndex].Name.Length; // GBK对应的code page是CP936
}
#endregion
for (int rowIndex = 0; rowIndex < list.Count; rowIndex++)
{
#region 新建sheet页,填充表头,填充列头,样式
if (rowIndex == 65535 || rowIndex == 0)
{
if (rowIndex != 0)
sheet = workbook.CreateSheet();
#region 表头及样式
{
IRow headerRow = sheet.CreateRow(0);
headerRow.HeightInPoints = 25;
headerRow.CreateCell(0).SetCellValue(titleText);
ICellStyle headStyle = workbook.CreateCellStyle();
headStyle.Alignment = HorizontalAlignment.Center;
IFont font = workbook.CreateFont();
font.FontHeightInPoints = 20;
font.Boldweight = 700;
headStyle.SetFont(font);
headerRow.GetCell(0).CellStyle = headStyle;
sheet.AddMergedRegion(new CellRangeAddress(0, 0, 0, properties.Length - 1));
}
#endregion
#region 列头及样式
{
IRow headerRow = sheet.CreateRow(1);
ICellStyle headStyle = workbook.CreateCellStyle();
headStyle.Alignment = HorizontalAlignment.Center;
headStyle.BorderBottom = BorderStyle.Thin;
headStyle.BorderLeft = BorderStyle.Thin;
headStyle.BorderRight = BorderStyle.Thin;
headStyle.BorderTop = BorderStyle.Thin;
headStyle.BottomBorderColor = HSSFColor.Black.Index;
headStyle.LeftBorderColor = HSSFColor.Black.Index;
headStyle.RightBorderColor = HSSFColor.Black.Index;
headStyle.TopBorderColor = HSSFColor.Black.Index;
headStyle.Alignment = HorizontalAlignment.Center;
IFont font = workbook.CreateFont();
font.FontHeightInPoints = 10;
font.Boldweight = 700;
headStyle.SetFont(font);
for (int columnIndex = 0; columnIndex < properties.Length; columnIndex++)
{
// 类属性如果有Description就用Description当做列名
DescriptionAttribute customAttribute = (DescriptionAttribute)Attribute.GetCustomAttribute(properties[columnIndex], typeof(DescriptionAttribute));
string description = properties[columnIndex].Name;
if (customAttribute != null)
description = customAttribute.Description;
headerRow.CreateCell(columnIndex).SetCellValue(description);
headerRow.GetCell(columnIndex).CellStyle = headStyle;
}
}
#endregion
}
#endregion
#region 填充内容
IRow dataRow = sheet.CreateRow(rowIndex + 2); // 前面2行已被占用
for (int columnIndex = 0; columnIndex < properties.Length; columnIndex++)
{
ICellStyle contentStyle = workbook.CreateCellStyle();
contentStyle.BorderBottom = BorderStyle.Thin;
contentStyle.BorderLeft = BorderStyle.Thin;
contentStyle.BorderRight = BorderStyle.Thin;
contentStyle.BorderTop = BorderStyle.Thin;
contentStyle.BottomBorderColor = HSSFColor.Black.Index;
contentStyle.LeftBorderColor = HSSFColor.Black.Index;
contentStyle.RightBorderColor = HSSFColor.Black.Index;
contentStyle.TopBorderColor = HSSFColor.Black.Index;
contentStyle.VerticalAlignment = VerticalAlignment.Top;
//contentStyle.WrapText = true;//是否换行
contentStyle.Alignment = HorizontalAlignment.Center;//居中
//contentStyle.Alignment = HorizontalAlignment.Left; //靠左
int columnWidth = sheet.GetColumnWidth(columnIndex) / 256;
for (int rowNum = 1; rowNum <= sheet.LastRowNum; rowNum++)
{
IRow currentRow;
//当前行未被使用过
if (sheet.GetRow(rowNum) == null)
currentRow = sheet.CreateRow(rowNum);
else
currentRow = sheet.GetRow(rowNum);
string widthValue = string.Empty;
if (properties[columnIndex].GetValue(list[rowIndex], null) != null)
widthValue = properties[columnIndex].GetValue(list[rowIndex], null).ToString();
if (currentRow.GetCell(columnIndex) != null)
{
ICell currentCell = currentRow.GetCell(columnIndex);
int length = Encoding.Default.GetBytes(widthValue).Length;
if (columnWidth < length)
columnWidth = length;
}
}
if (colWidthDic != null && colWidthDic.ContainsKey(columnIndex))
colWidthDic.TryGetValue(columnIndex, out columnWidth); //设置某些特定列的宽度
//设置列宽
sheet.SetColumnWidth(columnIndex, columnWidth * 256);
ICell newCell = dataRow.CreateCell(columnIndex);
newCell.CellStyle = contentStyle;
string drValue = "";
if (properties[columnIndex].GetValue(list[rowIndex], null) != null)
{
drValue = properties[columnIndex].GetValue(list[rowIndex], null).ToString();
}
switch (properties[columnIndex].PropertyType.ToString())
{
case "System.String":
newCell.SetCellValue(drValue);
break;
case "System.DateTime":
case "System.Nullable`1[System.DateTime]":
newCell.SetCellValue(drValue.ParseToDateTime());
newCell.CellStyle = dateStyle; //格式化显示
break;
case "System.Boolean":
case "System.Nullable`1[System.Boolean]":
newCell.SetCellValue(drValue.ParseToString());
break;
case "System.Byte":
case "System.Nullable`1[System.Byte]":
case "System.Int16":
case "System.Nullable`1[System.Int16]":
case "System.Int32":
case "System.Nullable`1[System.Int32]":
newCell.SetCellValue(drValue.ParseToInt());
break;
case "System.Int64":
case "System.Nullable`1[System.Int64]":
newCell.SetCellValue(drValue.ParseToString());
break;
case "System.Double":
case "System.Nullable`1[System.Double]":
newCell.SetCellValue(drValue.ParseToDouble());
break;
case "System.Decimal":
case "System.Nullable`1[System.Decimal]":
newCell.SetCellValue(drValue.ParseToDouble());
break;
case "System.DBNull":
newCell.SetCellValue(string.Empty);
break;
default:
newCell.SetCellValue(string.Empty);
break;
}
}
#endregion
}
}
catch (Exception ex)
{
workbook.Clear();
throw ex;
}
MemoryStream file = new MemoryStream();
workbook.Write(file);
return file;
}
/// <summary>
/// 导入excel
/// </summary>
/// <param name="httpPostedFileBase">excel文件</param>
/// <param name="sheetName">sheet名称</param>
/// <param name="headerRowIndex">表头所在行下标</param>
/// <param name="startRowIndex">数据开始行下标</param>
/// <param name="sheetIndex">sheet页下标</param>
/// <returns>List<T></returns>
public static List<T> GetExcelData(HttpPostedFileBase httpPostedFileBase, out string errMsg, string sheetName = null, int headerRowIndex = 0, int startRowIndex = 1, int sheetIndex = 0)
{
List<T> dataList = new List<T>();// 定义集合
Stream fileStream = httpPostedFileBase.InputStream;
IWorkbook Workbook;
int FileLen = httpPostedFileBase.ContentLength;
byte[] bytes = new byte[FileLen];
fileStream.Read(bytes, 0, FileLen); // 读取文件的 byte[]
string fileExt = httpPostedFileBase.FileName.Substring(httpPostedFileBase.FileName.LastIndexOf(".") + 1);
if (fileExt == "xls")
Workbook = new HSSFWorkbook(fileStream); //HSSFWorkbook 适用XLS格式
else if (fileExt == "xlsx")
Workbook = WorkbookFactory.Create(fileStream); //XSSFWorkbook 适用XLSX格式
else
{
errMsg = "请上传.xls/.xlsx格式文件,导入失败!!!";
return null;
}
try
{
//默认定位在第一个sheet; sheet页下标大于0 则获取对应sheet页下标数据
ISheet sheet = Workbook.GetSheetAt(sheetIndex);
if (!string.IsNullOrEmpty(sheetName)) // sheet页名称不为空 则获取对应sheet页数据
sheet = Workbook.GetSheet(sheetName);
//第一行为标题行
IRow headerRow = sheet.GetRow(headerRowIndex);
int cellCount = headerRow.LastCellNum; // 总列数
int rowCount = sheet.LastRowNum; // 总行数
List<string> colNameArr = new List<string>();
循环添加标题列
for (int i = headerRow.FirstCellNum; i < cellCount; i++)
{
colNameArr.Add(headerRow.GetCell(i).StringCellValue);
}
//把数据添加到dataList
for (int i = startRowIndex; i <= rowCount; i++)
{
IRow row = sheet.GetRow(i);
T tEntity = new T();
PropertyInfo[] propertys = GetColNameList(tEntity.GetType().GetProperties(), colNameArr, cellCount);// 获得此模型的公共属性
if (row != null)
{
for (int j = row.FirstCellNum; j < cellCount; j++)
{
var cellVal = row.GetCell(j);
if (cellVal != null)
{
var objVal = GetCellValue(cellVal);
if (cellVal.CellType == CellType.Numeric && propertys[j].PropertyType.ToString() == "System.String")
propertys[j].SetValue(tEntity, objVal.ToString(), null);
else
propertys[j].SetValue(tEntity, objVal, null);
}
}
}
dataList.Add(tEntity);
}
errMsg = "数据导入成功!!!";
}
catch (Exception ex)
{
errMsg = ex.ToString() + ",数据导入失败!!!";
}
finally
{
fileStream.Close();
Workbook.Clear();
}
return dataList;
}
private static object GetCellValue(ICell cell)
{
if (cell == null)
return string.Empty;
switch (cell.CellType)
{
case CellType.Blank:
return string.Empty;
case CellType.Boolean:
return cell.BooleanCellValue;
case CellType.Error:
return cell.ErrorCellValue.ToString();
case CellType.Numeric:
if (DateUtil.IsCellDateFormatted(cell))
return cell.DateCellValue;
else
return cell.NumericCellValue;
case CellType.Unknown:
default:
return cell.ToString();
case CellType.String:
return cell.StringCellValue;
case CellType.Formula:
try
{
HSSFFormulaEvaluator e = new HSSFFormulaEvaluator(cell.Sheet.Workbook);
e.EvaluateInCell(cell);
return cell.ToString();
}
catch
{
return cell.NumericCellValue.ToString();
}
}
}
/// <summary>
/// 反射出有添加Description所有属性
/// </summary>
/// <param name="propertys">实体的所以属性列表</param>
/// <param name="colNameArr">需要导入的表字段</param>
/// <param name="cellCount">导入字段数量</param>
/// <returns></returns>
private static PropertyInfo[] GetColNameList(PropertyInfo[] propertys, List<string> colNameArr, int cellCount)
{
PropertyInfo[] propertyList = new PropertyInfo[cellCount];
foreach (PropertyInfo pi in propertys)
{
//校验实体属性是否有添加Description
DescriptionAttribute entityAttribute = (DescriptionAttribute)Attribute.GetCustomAttribute(pi, typeof(DescriptionAttribute));
string colName = pi.Name;
if (entityAttribute != null)
colName = entityAttribute.Description;
if (colNameArr.Contains(colName)) //判断属性是否在导入列表中
{
for (int i = 0; i < colNameArr.Count; i++)
{
if (colNameArr[i] == colName)
propertyList[i] = pi; //属性在列表中则添加到数组
}
}
}
return propertyList;
}
}
}
个人测试了一下性能,这个方案比写datatable要快一丢丢!!!!!!!!!!
有需要的老铁直接复制到项目中即可,有更好的方案也希望各位老铁们分享给我学习学习,谢谢!!!