WeihanLi.Npoi 1.21.0 Released

WeihanLi.Npoi 1.21.0 Released

Intro

WeihanLi.Npoi 是一个基于 netstandard2.0 的一个 NPOI 扩展库,主要用于导入导出 Excel 以及 CSV,支持通过 Fluent API 的方式来支持非常灵活的导入导出配置,详细使用可以参考文档介绍以及项目示例 https://github.com/WeihanLi/WeihanLi.Npoi

New Features

本次引入的新功能是针对 DataTable 的优化,如果导入的 Excel 出现了重复列,原来会直接抛出一个 System.Data.DuplicateNameException,主要是因为原来是直接用 excel 列名称作为 DataColumnName,而一个 DataTable 中是不允许有名字重复 Column 的,就像数据库同一个表中不允许出现重复列名一样。可以参考 Issue https://github.com/WeihanLi/WeihanLi.Npoi/issues/125

而导入 excel 时,很多时候可能并不根据列名称去读取对应的值,有时候会直接使用列索引来读取列的值,这个场景下,即使 excel 列名冲突了也关系不大,我们只需要按照索引读取就可以了,所以就考虑了支持冲突的读取,因为想再导出的时候 excel 还和之前导入的时候保持一致,所以也增加了导出的时候对 DataTable 的处理,实现效果可以参考单元测试:

// Csv
[Fact]
public void DuplicateColumnTest()
{
    var csvText = $@"A,B,C,A,B,C{Environment.NewLine}1,2,3,4,5,6";
    var dataTable = CsvHelper.ToDataTable(csvText.GetBytes());
    Assert.Equal(6, dataTable.Columns.Count);
    Assert.Equal(1, dataTable.Rows.Count);

    var newCsvText = CsvHelper.GetCsvText(dataTable);
    Assert.StartsWith("A,B,C,A,B,C", newCsvText);
    var newDataTable = CsvHelper.ToDataTable(newCsvText.GetBytes());

    Assert.Equal(dataTable.Columns.Count, newDataTable.Columns.Count);
    Assert.Equal(dataTable.Rows.Count, newDataTable.Rows.Count);
}
// Excel
[Theory]
[ExcelFormatData]
public void DuplicateColumnTest(ExcelFormat excelFormat)
{
    var workbook = ExcelHelper.PrepareWorkbook(excelFormat);
    var sheet = workbook.CreateSheet();
    var headerRow = sheet.CreateRow(0);
    headerRow.CreateCell(0).SetCellValue("A");
    headerRow.CreateCell(1).SetCellValue("B");
    headerRow.CreateCell(2).SetCellValue("C");
    headerRow.CreateCell(3).SetCellValue("A");
    headerRow.CreateCell(4).SetCellValue("B");
    headerRow.CreateCell(5).SetCellValue("C");
    var dataRow = sheet.CreateRow(1);
    dataRow.CreateCell(0).SetCellValue("1");
    dataRow.CreateCell(1).SetCellValue("2");
    dataRow.CreateCell(2).SetCellValue("3");
    dataRow.CreateCell(3).SetCellValue("4");
    dataRow.CreateCell(4).SetCellValue("5");
    dataRow.CreateCell(5).SetCellValue("6");
    
    var dataTable = sheet.ToDataTable();
    Assert.Equal(headerRow.Cells.Count, dataTable.Columns.Count);
    Assert.Equal(1, dataTable.Rows.Count);

    var newWorkbook = ExcelHelper.LoadExcel(dataTable.ToExcelBytes());
    var newSheet = newWorkbook.GetSheetAt(0);
    Assert.Equal(sheet.PhysicalNumberOfRows, newSheet.PhysicalNumberOfRows);
    for (var i = 0; i < sheet.PhysicalNumberOfRows; i++)
    {
        Assert.Equal(sheet.GetRow(i).Cells.Count, newSheet.GetRow(i).Cells.Count);

        for (var j = 0; j < headerRow.Cells.Count; j++)
        {
            Assert.Equal(
                sheet.GetRow(i).GetCell(j).GetCellValue<string>(),
                newSheet.GetRow(i).GetCell(j).GetCellValue<string>()
            );
        }
    }
}

实现方式上一定程度参考了 issue 给出的建议,导入时重复列会添加一个 duplicate 标识和一个唯一 id 使得名称不会重复,从而不会引发异常,导出时如果是重复列会把 duplicate 标识和唯一 id 去掉从而还原真实的列名称,更多细节可以查看 Github 上的 PR https://github.com/WeihanLi/WeihanLi.Npoi/pull/126

Bug Fixes

修复了 sheet name 配置可能会不生效的 BUG

本次更新修复了在导出成文件的时候 sheet name 的配置没有生效的一个 BUG,详细可以参考 issue: https://github.com/WeihanLi/WeihanLi.Npoi/issues/127

开始并没有重现这个 BUG,因为只有在导出为文件的时候才会有问题,如果是 bytes 或者 stream 是不会有这个问题的,现在已经增加了下面的测试用例来覆盖这个情况

[Theory]
[ExcelFormatData]
public void SheetNameTest_ToExcelFile(ExcelFormat excelFormat)
{
    IReadOnlyList<Notice> list = Enumerable.Range(0, 10).Select(i => new Notice()
    {
        Id = i + 1,
        Content = $"content_{i}",
        Title = $"title_{i}",
        PublishedAt = DateTime.UtcNow.AddDays(-i),
        Publisher = $"publisher_{i}"
    }).ToArray();
    var settings = FluentSettings.For<Notice>();
    lock (settings)
    {
        settings.HasSheetSetting(s =>
        {
            s.SheetName = "Test";
        });

        var filePath = $"{Path.GetTempFileName()}.{excelFormat.ToString().ToLower()}";
        list.ToExcelFile(filePath);

        var excel = ExcelHelper.LoadExcel(filePath);
        Assert.Equal("Test", excel.GetSheetAt(0).SheetName);

        settings.HasSheetSetting(s =>
        {
            s.SheetName = "NoticeList";
        });
    }


}

[Theory]
[ExcelFormatData]
public void SheetNameTest_ToExcelBytes(ExcelFormat excelFormat)
{
    IReadOnlyList<Notice> list = Enumerable.Range(0, 10).Select(i => new Notice()
    {
        Id = i + 1,
        Content = $"content_{i}",
        Title = $"title_{i}",
        PublishedAt = DateTime.UtcNow.AddDays(-i),
        Publisher = $"publisher_{i}"
    }).ToArray();
    var settings = FluentSettings.For<Notice>();
    lock (settings)
    {
        settings.HasSheetSetting(s =>
        {
            s.SheetName = "Test";
        });

        var excelBytes = list.ToExcelBytes(excelFormat);
        var excel = ExcelHelper.LoadExcel(excelBytes, excelFormat);
        Assert.Equal("Test", excel.GetSheetAt(0).SheetName);

        settings.HasSheetSetting(s =>
        {
            s.SheetName = "NoticeList";
        });
    }
}

修复导出到文件 excel 文件格式不对的 BUG

根据文件路径创建 excel workbook 的时候原来是有 BUG 的可能会导致文件格式不对,原来没有先换取文件扩展名,新版本中修复了这个 bug,会先获取文件扩展名再判断文件格式

- !excelPath.EqualsIgnoreCase(".xls")
+ !Path.GetExtension(excelPath).EqualsIgnoreCase(".xls")

More

这个新版本中还有个针对 CsvHelper 的小优化,主要是获取导出的 CSV 字符串时 includeHeader 参数变成了一个可选参数,对于调用方来说可以调用会变得更简单一些,默认值是 true,默认会包含 header

public static string GetCsvText(this DataTable? dataTable, bool includeHeader = true);
public static string GetCsvText<TEntity>(this IEnumerable<TEntity> entities, bool includeHeader = true);
更多细节可以参考 PR 变更 https://github.com/WeihanLi/WeihanLi.Npoi/pull/130

References

  • https://github.com/WeihanLi/WeihanLi.Npoi

  • https://github.com/WeihanLi/WeihanLi.Npoi/pull/130

  • https://www.nuget.org/packages/WeihanLi.Npoi/1.21.0

  • https://github.com/WeihanLi/WeihanLi.Npoi/tree/1.21.0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值