選擇第三方庫
- Epplus : 5.0后出現授權問題 out
- Magicodes.IE : 基於Epplush 4.x , IExporterHeaderFilter 這個的性能很渣,而且動態調整order會出現順序錯亂 out
- NPOI : 個人不是太喜歡
- ClosedXML : 基於 Document.OpenXML ,性能不錯但是需要自己封裝一些業務功能 i like this
簡單導出功能
//導出
protected override void Action()
{
//創建workbook
var wb = new XLWorkbook(XLEventTracking.Disabled);
wb.Worksheets.Add("test", Datas);
wb.SaveAs($"{Guid.NewGuid()}.xlsx");
}
internal static class ClosedXMLExtension
{
//添加IXLWorksheet擴展 你也可以基於函數封裝
public static IXLWorksheet Add<T>(this IXLWorksheets wss, string sheetName, IEnumerable<T> datas)
{
var ws = wss.Add(sheetName);
var props = typeof(T).GetProperties();
int row = 1, col = 1;
foreach (var data in datas)
{
foreach (var prop in props)
{
ws.Cell(row, col).Value = prop.GetValue(data);
col++;
}
col = 1;
row++;
}
return ws;
}
}
進一步封裝
說明
- 整體使用反射獲取值向cell中填充
- header可以動態控制,但是控制流實現的比較簡單
- 使用model字段的小寫與動態header進行匹配
public static class ClosedXMLExtension
{
/// <summary>
/// 向單個sheet填充數據
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="wss">IXLWorksheets</param>
/// <param name="sheetName">sheet 名稱(需要唯一)</param>
/// <param name="dataSource">數據源</param>
/// <returns></returns>
public static IXLWorksheet Add<T>(this IXLWorksheets wss, string sheetName, IEnumerable<T> dataSource, Dictionary<string, DynamicHeader> headers = null)
{
if (dataSource == null)
throw new ArgumentNullException(nameof(dataSource), "datasource cant be null");
if (string.IsNullOrEmpty(sheetName))
throw new ArgumentNullException(nameof(dataSource), "sheet's name cant be null");
//創建sheet
var ws = wss.Add(sheetName);
//如果header中沒有數據,直接返回
if (headers != null && !headers.Any())
return ws;
var props = typeof(T).GetProperties();
headers = ChangeDynamicHeadersOrder(headers);
AddHeader(ws, headers == null ?
props.Select(x => x.Name) :
headers.Select(x => x.Value.DisplayName));
int row = 2, col = 1;
foreach (var data in dataSource)
{
foreach (var prop in props)
{
if (headers == null)
{
ws.Cell(row, col).Value = prop.GetValue(data);
col++;
}
else if (headers.TryGetValue(prop.Name.ToLower(), out var dynamicHeader))
ws.Cell(row, dynamicHeader.Index).Value = prop.GetValue(data);
}
col = 1;
row++;
}
return ws;
}
/// <summary>
/// 添加表格頭
/// </summary>
/// <param name="ws"></param>
/// <param name="headerNames"></param>
private static void AddHeader(IXLWorksheet ws, IEnumerable<string> headerNames)
{
int row = 1, col = 1;
foreach (var name in headerNames)
{
var cell = ws.Cell(row, col);
cell.Value = name;
cell.Style.Font.Bold = true;
cell.Style.Fill.SetBackgroundColor(XLColor.LightBlue);
col++;
}
}
/// <summary>
/// 修改header的順序防止數據重疊
/// </summary>
/// <param name="headers"></param>
/// <returns></returns>
private static Dictionary<string, DynamicHeader> ChangeDynamicHeadersOrder(Dictionary<string, DynamicHeader> headers)
{
if (headers == null) return null;
var temp = headers
.OrderBy(x => x.Value.Index)
.Select((x, index) =>
new KeyValuePair<string, DynamicHeader>(
x.Key.ToLower(),
new DynamicHeader() { Index = index + 1, DisplayName = x.Value.DisplayName }
));
return temp.ToDictionary(x => x.Key, x => x.Value);
}
}
public class DynamicHeader
{
/// <summary>
/// 序號
/// </summary>
public int Index { get; set; }
/// <summary>
/// Header顯示名稱
/// </summary>
public string DisplayName { get; set; }
}