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

崩壞的領航員發表於2021-11-16

基於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生成配置
    • 可以可存入一般關係型資料庫的資料生成配置

相關文章