基於EPPlus和NPOI實現的Excel導入導出

基於EPPlus和NPOI實現的Excel導入導出

CollapseNav.Net.Tool.Excel(NuGet地址)

太長不看

  • 導入
    • excel 文件流將會轉為 ExcelTestDto 類型的集合
    • var config = new ReadConfig<ExcelTestDto>()
      .Default(item => item.Field0, "233")
      .Require("Field1", item => item.Field1)
      .Add("Field3", item => item.Field3);
      IEnumerable<ExcelTestDto> data = await config.EPPlusExcelToEntityAsync(excelStream);
      
  • 導出
    • ExcelTestDto 類型的集合將會轉為 excel 文件流
    • var config = new ExportConfig<ExcelTestDto>(datas);
      .Add("Field0", item => item.Field0)
      .Add("Field1", item => item.Field1)
      .Add("Field2", item => item.Field2 ? "Male" : "Female")
      .Add("Field3", item => item.Field3);
      Stream stream = await exportConfig.EPPlusExportAsync();
      

簡單的使用方式就是上面那樣, 至少在我需要的使用場景下是work的

NuGet包在上面

前言

為了方便自己處理一些有關 excel 的導入導出功能, 所以花了更長時間做了這個包

主要是我覺得計算各個未知的下標位置, 計算那些 0 1 2 3 4 5 實在是太麻煩

而且每換一個模板就要再搞一次, 可讀性巨差

我想擺脫這樣的地獄, 然後奮力掙扎了幾下

思路可以看一下之前的文章

本章就講怎麼用這個包

Excel Data

使用的表格數據Demo, 暫時只能處理單行表頭的簡單excel

Field0 Field1 Field2 Field3
233 23 Male 233.33
1122 12 Female 123.23
233 23 Male 233.33
1122 12 Female 123.23

How To Use

導入(Import/Read/…)

我碰到的使用場景中, 一般需要將Excel的數據轉為系統中的某個實體

比如導入一個商品列表Excel,將這個列錶轉為 Goods 對象, 然後使用現成的 AddRange(IEnumerable<Goods> datas) 方法存到數據庫中

基於以上這種 Excel-->Entitys 的使用場景設計了這一套東西

性能怎麼樣我就沒測試了, 可能很拉了

假設我的實體長這樣

public class ExcelTestDto
{
    public string Field0 { get; set; }
    public int Field1 { get; set; }
    public bool Field2 { get; set; }
    public double Field3 { get; set; }
}

1.BuildReadConfig

第一步先創建一個 ReadConfig 作為excel的讀取配置

var config = new ReadConfig<ExcelTestDto>();

這個配置決定了之後將以什麼方式讀取哪些列

2.AddCellOptions

有了 ReadConfig 之後就需要添加具體的配置了

暫時提供了 Default Require Add 添加對 單個實體字段 的讀取設置

  • Default
    • 不依賴表格數據,對 ExcelTestDto 中的屬性統一添加默認值
    • config.Default(item => item.Field0, "233");
      
  • Require
    • 被 Require 的單元格不可為空, 否則在讀取時會主動拋出異常
    • config.Require("Field1", item => item.Field1);
      
  • Add
    • 普通的添加單元格設置, 相對來說更加靈活一些
    • config.Add("Field3", item => item.Field3)
      

所有的excel單元格都會被讀成 string

Require Add 都可以使用 Func<string, object> 委託自定義對讀取單元格的處理

由於是委託, 你可以做很多操作, 但比較容易影響性能, 最好不要寫複雜的耗時的委託

config.Add("Field1", item => item.Field1, item =>
{
    var numCellData = int.Parse(item);
    numCellData += 2333;
    return numCellData;
});

以上操作都會返回 ReadConfig , 所以 強烈推薦 寫成以下的調用

var config = new ReadConfig<ExcelTestDto>()
.Default(item => item.Field0, "233")
.Require("Field1", item => item.Field1)
.Add("Field3", item => item.Field3)
;

同時提供了對應的 DefaultIf RequireIf AddIf 方法, 用來根據不同的條件添加不同的配置

3.AddInit

偶爾會有在一行數據讀取完之後計算點什麼的需求, 比如綜合學生的各科成績打個等第, 所以提供一個 AddInit 方法, 通過傳入一個 Func<T, T> 來搞點事情

config.AddInit(item =>
{
    item.Field0 += "23333";
    // 一些屬性的初始化也可以在這邊做,代替 Default 也是可以的
    item.Field2 = false;
    return item;
});

4.ConvertExcel

配置完成之後就可以使用 EPPlusExcelToEntityAsync 將對應的excel轉為實體集合

// 如果excel是個文件流
IEnumerable<ExcelTestDto> data = await config.EPPlusExcelToEntityAsync(excelStream);

除了流, 也支持其他的參數

  • string filepath
    • 簡單質樸的物理文件路徑, 將直接讀取物理路徑上的excel文件
  • ExcelPackage pack
    • EPPlusExcelPackage, 一般需要手動創建
  • ExcelWorksheet sheet
    • EPPlusExcelWorksheet, 一般需要手動創建

導出(Export/…)

有的時候總是會有人需要把系統裏面的列表數據導出成 Excel

然後像個傻逼一樣再導回到系統裏面去

所以相對導入又做了個導出功能, 兩者相似度比較高

1.BuildExportConfig

建個導出配置 ExportConfig

// datas 為 ExcelTestDto集合
var config = new ExportConfig<ExcelTestDto>(datas);

由於導出比較簡單粗暴, 所以提供了一個 GenDefaultConfig 可以直接 根據泛型生成 Config (大概不算好用)

2.AddCellOption

由於導出比較簡單粗暴, 所以就只有一個 AddAddIf 方法添加單元格設置(雖然是兩個)

config
.Add("Field0", item => item.Field0)
.Add("Field1", item => item.Field1)
.Add("Field2", item => item.Field2 ? "Male" : "Female")
.Add("Field3", item => item.Field3);

3.GenerateExcel

使用 EPPlusExportAsync 生成 Excel

// 新版本應該已經支持無參導出為流
// Stream stream = await exportConfig.EPPlusExportAsync();
Stream stream = await exportConfig.EPPlusExportAsync(someStream);

方法會返回一個流, 拿到流之後可以去做你們想做的事情…

也可以傳入一個物理路徑(string 類型), 這樣就會在指定的位置生成excel

TODO

  • [x] 無參導出
  • [ ] 合併相同的導入配置
  • [ ] 考慮添加錯誤處理
  • [ ] 測試性能問題
  • [ ] 根據配置生成導入導出配置
    • 根據泛型的屬性生成配置
    • 根據attribute生成配置
    • 可以可存入一般關係型數據庫的數據生成配置