当前位置:首页 > 后端开发 > 正文内容

开源 - Ideal库 - Excel协助类,ExcelHelper完成(五)

邻居的猫1个月前 (12-09)后端开发1957

书接上回,咱们持续来聊聊ExcelHelper的具体完结。

01、读取Excel到DataSet单元测验

在上一章咱们首要讲解了读取Excel到DataSet的三个重载办法具体完结,还没来得及做单元测验,因而咱们首要对这三个办法做个单元测验。具体代码如下:

[Fact]
public void Read_FileName_DataSet()
{
    //读取一切作业簿
    var dataSet = ExcelHelper.Read("Read.xlsx");
    Assert.Equal(3, dataSet.Tables.Count);
    var table1 = dataSet.Tables[0];
    Assert.Equal("Sheet1", table1.TableName);
    Assert.Equal("A", table1.Rows[0][0]);
    Assert.Equal("B", table1.Rows[0][1]);
    Assert.Equal("1", table1.Rows[0][2]);
    Assert.Equal("C", table1.Rows[1][0]);
    Assert.Equal("D", table1.Rows[1][1]);
    Assert.Equal("2", table1.Rows[1][2]);

    //读取一切作业簿,而且首行数据作为表头
    dataSet = ExcelHelper.Read("Read.xlsx", true);
    Assert.Equal(3, dataSet.Tables.Count);
    table1 = dataSet.Tables[1];
    var columus = table1.Columns;
    Assert.Equal("Sheet2", table1.TableName);
    Assert.Equal("E", columus[0].ColumnName);
    Assert.Equal("F", columus[1].ColumnName);
    Assert.Equal("3", columus[2].ColumnName);
    Assert.Equal("G", table1.Rows[0][0]);
    Assert.Equal("H", table1.Rows[0][1]);
    Assert.Equal("4", table1.Rows[0][2]);

    //依据作业簿称号sheetName读取指定作业簿
    dataSet = ExcelHelper.Read("Read.xlsx", true, "Sheet2");
    Assert.Single(dataSet.Tables);
    Assert.Equal("Sheet2", dataSet.Tables[0].TableName);

    //经过作业簿称号sheetName读取不存在的作业簿
    dataSet = ExcelHelper.Read("Read.xlsx", true, "Sheet99");
    Assert.Empty(dataSet.Tables);

    //一起指定sheetName和sheetNumber优先运用sheetName
    dataSet = ExcelHelper.Read("Read.xlsx", true, "Sheet1", 2);
    Assert.Single(dataSet.Tables);
    Assert.Equal("Sheet1", dataSet.Tables[0].TableName);
    //经过作业簿编号sheetNumber读取不存在的作业簿

    dataSet = ExcelHelper.Read("Read.xlsx", true, null, 99);
    Assert.Empty(dataSet.Tables);

    //经过作业簿编号sheetNumber读取指定作业簿
    dataSet = ExcelHelper.Read("Read.xlsx", true, null, 1);
    Assert.Single(dataSet.Tables);
    Assert.Equal("Sheet1", dataSet.Tables[0].TableName);
}
# ***02***、依据文件途径读取Excel到目标调集```

在上一章中咱们完结了Excel与DataSet彼此转化,而在前面TableHelper完结章节中咱们现已完结了目标调集与表格DataTable的彼此转化,因而咱们只要把这两者结合起来就能够完结Excel与目标调集的彼此转化。

由于Excel中有多个作业簿Sheet,而每一个作业簿Sheet代表一个表格DataTable,一个表格DataTable相关一个目标调集,因而咱们约定本办法有必要指定一个作业簿Sheet用来转化目标调集,假如没有指定则默许读取榜首个作业簿Sheet。

而该办法经过文件彻底途径读取到Excel文件流后,调用具体完结文件流处理重载办法,具体代码如下:

```csharp
//依据文件途径读取Excel到目标调集
//指定sheetName,sheetNumber则读取相应作业簿Sheet
//假如不指定则默许读取榜首个作业簿Sheet
public static IEnumerable<T> Read<T>(string path, bool isFirstRowAsColumnName = false, string? sheetName = null, int? sheetNumber = null)
{
    using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
    return Read<T>(stream, IsXlsxFile(path), isFirstRowAsColumnName, sheetName, sheetNumber);
}

03、依据文件流、文件名读取Excel到目标调集

在有些场景下,咱们直接得到的便是Excel文件流,因而更通用的处理办法便是处理ExceL文件流,由于无论如何终究咱们都是要拿到Excel文件流的。

该办法也是一个重载办法,为了便利哪些上传文件后,有文件流,有文件名,可是不想自己处理文件后缀格局的,供给一个快捷办法,因而该办法会经过文件名辨认出文件具体后缀格局,再调用下一个重载办法,具体完结如下:

//依据文件流读取Excel到目标调集
//指定sheetName,sheetNumber则读取相应作业簿Sheet
//假如不指定则默许读取榜首个作业簿Sheet
public static IEnumerable<T> Read<T>(Stream stream, string fileName, bool isFirstRowAsColumnName = false, string? sheetName = null, int? sheetNumber = null)
{
    return Read<T>(stream, IsXlsxFile(fileName), isFirstRowAsColumnName, sheetName, sheetNumber);
}

04、依据文件流、文件后缀读取Excel到目标调集

该办法是上面两个办法的终究完结,具体完结分为两步:

(1)读取指定作业簿Sheet到DataSet中;

(2)把DataSet中榜首个表格DataTable转化为目标调集;

而这两步都是调用之前完结好的办法,具体代码如下:

//依据文件流读取Excel到目标调集
//指定sheetName,sheetNumber则读取相应作业簿Sheet
//假如不指定则默许读取榜首个作业簿Sheet
public static IEnumerable<T> Read<T>(Stream stream, bool isXlsx, bool isFirstRowAsColumnName = false, string? sheetName = null, int? sheetNumber = null)
{
    //读取指定作业簿Sheet至DataSet
    var dataSet = CreateDataSetWithStreamOfSheet(stream, isXlsx, isFirstRowAsColumnName, sheetName, sheetNumber ?? 1);
    if (dataSet == null || dataSet.Tables.Count == 0)
    {
        return [];
    }

    //DataTable转目标调集
    return TableHelper.ToModels<T>(dataSet.Tables[0]);
}

下面咱们针对上面三个办法做个简略的单元测验,代码如下:

public class Student
{
    public string A { get; set; }
    [Description("B")]
    public string Name { get; set; }
    [Description("1")]
    public DateTime Age { get; set; }
}

[Fact]
public void Read_FileName_T()
{
    //表格数据格局无法转为目标数据类型,则抛反常
    Assert.Throws<FormatException>(() => ExcelHelper.Read<Student>("Read.xlsx", true, "Sheet1"));

    //表格成功转为目标调集
    var models = ExcelHelper.Read<Student>("Read.xlsx", true, "Sheet3");
    Assert.Single(models);
    var model = models.First();
    Assert.Equal("C", model.A);
    Assert.Equal("D", model.Name);
    Assert.Equal(new DateTime(2024, 11, 29), model.Age);
}

05、把表格数组写入Excel文件流

该办法是先把表格数组生成Excel的IWorkbook,然后再写入内存流MemoryStream。

而表格数组转化为IWorkbook也很简略,在IWorkbook中创立作业簿Sheet,然后把每个表格数据填充至相应的作业簿Sheet中即可,具体代码如下:

//把表格数组写入Excel文件流
public static MemoryStream Write(DataTable[] dataTables, bool isXlsx, bool isColumnNameAsData)
{
    //表格数组写入Excel目标
    using var workbook = CreateWorkbook(dataTables, isXlsx, isColumnNameAsData);
    var stream = new MemoryStream();
    workbook.Write(stream, true);
    stream.Flush();
    return stream;
}

//表格数组转为IWorkbook
private static IWorkbook CreateWorkbook(DataTable[] dataTables, bool isXlsx, bool isColumnNameAsData)
{
    //依据Excel文件后缀创立IWorkbook
    var workbook = CreateWorkbook(isXlsx);
    foreach (var dt in dataTables)
    {
        //依据表格填充Sheet
        FillSheetByDataTable(workbook, dt, isColumnNameAsData);
    }

    return workbook;
}

而依据表格填充作业簿Sheet完结也十分简略,只需遍历表格中每个单元格,把其值填充至对应作业簿Sheet中相同的方位即可,当然其间表格列名是否要作为数据,需求独自处理,具体代码如下:

//依据表格填充作业簿Sheet
private static void FillSheetByDataTable(IWorkbook workbook, DataTable dataTable, bool isColumnNameAsData)
{
    var sheet = string.IsNullOrWhiteSpace(dataTable.TableName) ? workbook.CreateSheet() : workbook.CreateSheet(dataTable.TableName);
    if (isColumnNameAsData)
    {
        //把列名参加数据榜首行
        var dataRow = sheet.CreateRow(0);
        foreach (DataColumn column in dataTable.Columns)
        {
            dataRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
        }
    }

    //循环处理表格的一切行数据
    for (var i = 0; i < dataTable.Rows.Count; i++)
    {
        var dataRow = sheet.CreateRow(i + (isColumnNameAsData ? 1 : 0));
        for (var j = 0; j < dataTable.Columns.Count; j++)
        {
            dataRow.CreateCell(j).SetCellValue(dataTable.Rows[i][j].ToString());
        }
    }
}

06、把表格数组写入Excel文件

该办法需求留意的是关于Excel文件途径的处理,假如给定的Excel文件途径不存在,则本办法会主动创立相应的文件夹,假如给定的Excel文件途径中不包括文件称号,则本办法会主动依据当时时刻+4位随机数的办法+.xlsx的命名办法主动生成文件名。

处理好这些则只需求调用依据表格数组生成Excel目标办法,最终写入Excel文件中,具体代码如下:

//把表格数组写入Excel文件
public static void Write(DataTable[] dataTables, string path, bool isColumnNameAsData)
{
    //查看文件夹是否存在,不存在则创立
    var directoryName = Path.GetDirectoryName(path);
    if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
    {
        Directory.CreateDirectory(directoryName);
    }

    //查看是否指定文件名,没有则默许以“时刻+随机数.xlsx”作为文件名
    var fileName = Path.GetFileName(path);
    if (string.IsNullOrEmpty(fileName))
    {
        directoryName = Path.GetFullPath(path);
        fileName = DateTime.Now.ToString("yyyyMMdd-hhmmss-") + new Random().Next(0000, 9999).ToString("D4") + ".xlsx";
        path = Path.Combine(directoryName, fileName);
    }

    //表格数组写入Excel目标
    using var workbook = CreateWorkbook(dataTables, IsXlsxFile(path), isColumnNameAsData);
    using var fs = new FileStream(path, FileMode.Create, FileAccess.Write);
    workbook.Write(fs, true);
}

下面咱们对上面两个写入办法进行具体的单元测验,具体如下:

[Fact]
public void Write_Table()
{
    var table = TableHelper.Create<Student>();
    var row1 = table.NewRow();
    row1[0] = "Id-11";
    row1[1] = "称号-12";
    row1[2] = new DateTime(2024, 11, 28);
    table.Rows.Add(row1);

    var row2 = table.NewRow();
    row2[0] = "Id-21";
    row2[1] = "称号-22";
    row2[2] = new DateTime(2024, 11, 29);
    table.Rows.Add(row2);

    var message = "The column name of the table cannot be mapped to an object property, and the conversion cannot be completed.";

    //把表格写入Excel,而且列名不作为数据行,成果从头读取Excel无法和目标完结转化
    ExcelHelper.Write([table], "Write.xls", false);
    var exception1 = Assert.Throws<NotSupportedException>(() => ExcelHelper.Read<Student>("Write.xls", true, "Sheet0"));
    Assert.Equal(message, exception1.Message);

    //把表格写入Excel,而且列名作为数据行,可是从头读取Excel时榜首行没有作为列名,成果仍是无法和目标完结转化
    ExcelHelper.Write([table], "Write.xls", true);
    var exception2 = Assert.Throws<NotSupportedException>(() => ExcelHelper.Read<Student>("Write.xls", false, "Sheet0"));
    Assert.Equal(message, exception2.Message);

    //从头读取Excel时榜首行作为列名
    var models = ExcelHelper.Read<Student>("Write.xls", true, "Sheet0");
    Assert.Equal(2, models.Count());
    var model = models.First();
    Assert.Equal("Id-11", model.A);
    Assert.Equal("称号-12", model.Name);
    Assert.Equal(new DateTime(2024, 11, 28), model.Age);
    File.Delete("Write.xls");
}

07、把目标调集写入Excel文件流或Excel文件

到这儿这两个办法就很好完结了,由于这两个办法需求的一切根底办法都现已完结,中心思路便是先把目标调集转化为表格DataTable,然后再经过调用相关把表格数组写入Excel的扩展办法完结即可,具体代码如下:

//把目标调集写入Excel文件流
public static MemoryStream Write<T>(IEnumerable<T> models, bool isXlsx, bool isColumnNameAsData, string? sheetName = null)
{
    //目标调集转为表格
    var table = TableHelper.ToDataTable<T>(models, sheetName);
    //表格数组写入Excel文件流
    return Write([table], isXlsx, isColumnNameAsData);
}

//把目标调集写入Excel文件
public static void Write<T>(IEnumerable<T> models, string path, bool isColumnNameAsData, string? sheetName = null)
{
    //目标调集转为表格
    var table = TableHelper.ToDataTable<T>(models, sheetName);
    //表格数组写入Excel文件
    Write([table], path, isColumnNameAsData);
}

最终咱们再进行一次具体的单元测验,代码如下:

[Fact]
public void Write_T()
{
    //验证正常状况
    var students = new List<Student>();
    var student1 = new Student
    {
        A = "Id-11",
        Name = "称号-12",
        Age = new DateTime(2024, 11, 28)
    };
    students.Add(student1);

    var student2 = new Student
    {
        A = "Id-21",
        Name = "称号-22",
        Age = new DateTime(2024, 11, 29)
    };
    students.Add(student2);

    var message = "The column name of the table cannot be mapped to an object property, and the conversion cannot be completed.";

    //把目标调集写入Excel,而且列名不作为数据行,成果从头读取Excel无法和目标完结转化
    ExcelHelper.Write<Student>(students, "Write_T.xls", false);
    var exception1 = Assert.Throws<NotSupportedException>(() => ExcelHelper.Read<Student>("Write_T.xls", true, "Sheet0"));
    Assert.Equal(message, exception1.Message);

    //把目标调集写入Excel,而且列名作为数据行,可是从头读取Excel时榜首行没有作为列名,成果仍是无法和目标完结转化
    ExcelHelper.Write<Student>(students, "Write_T.xls", true);
    var exception2 = Assert.Throws<NotSupportedException>(() => ExcelHelper.Read<Student>("Write_T.xls", false, "Sheet0"));
    Assert.Equal(message, exception2.Message);

    //从头读取Excel时榜首行作为列名
    var models = ExcelHelper.Read<Student>("Write_T.xls", true, "Sheet0");
    Assert.Equal(2, models.Count());
    var model = models.First();
    Assert.Equal("Id-11", model.A);
    Assert.Equal("称号-12", model.Name);
    Assert.Equal(new DateTime(2024, 11, 28), model.Age);
    File.Delete("Write_T.xls");
}

到这儿咱们整个Excel封装就完结了,信任经过目标调集完结Excel导入导出能满意大多数事务开发需求。当然假如有更杂乱的事务需求,还需求咱们自己去研讨相应的第三方库。

:测验办法代码以及示例源码都现已上传至代码库,有爱好的能够看看。https://gitee.com/hugogoos/Ideal

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=152

分享给朋友:

“开源 - Ideal库 - Excel协助类,ExcelHelper完成(五)” 的相关文章

r语言培训,开启数据科学之旅

1. 哔哩哔哩课程 小白速成与实践 全30课:这套课程包括30条视频,涵盖了R语言的基础知识,如R语言是什么、R的优势、资源、安装、获取帮助、工作空间管理、R包的使用等。 课程标准:提供从基础到高级的R语言教程,包括数据挖掘与R语言,以及各种统计分析方法的应用。2. 华大基因培训...

c语言程序设计,基础入门与进阶指南

好的,请问您想了解C语言程序设计的哪个方面?例如语法、数据结构、算法、编程实践等。我会尽力为您提供相关的信息。C语言程序设计:基础入门与进阶指南C语言作为一种历史悠久且广泛使用的编程语言,在计算机科学领域占据着举足轻重的地位。本文将为您介绍C语言程序设计的基础知识,帮助您从入门到进阶,掌握这一强大的...

delphi,从入门到精通

Delphi 是一种编程语言和集成开发环境(IDE),主要用于开发桌面、移动、Web 和控制台应用程序。它是由 Embarcadero Technologies(前身为 Borland)开发,最初在1995年推出。Delphi 是基于 Pascal 语言,但后来加入了面向对象编程的特性,使其成为了一...

567go,探索567go——您的智能出行新伙伴

567go,探索567go——您的智能出行新伙伴

567GO国际健身学院成立于2005年,隶属于北京全能奥菲特健身顾问有限公司,是中国知名的健身教育培训企业。学院以团体课程为核心,私人教练培训为重点,致力于为中国健身事业的发展贡献力量。567GO在全国范围内设有多个校区,包括北京、上海、广州、成都、西安、济南、杭州、大连、重庆、长沙、厦门、天津、南...

c语言volatile的作用,深入解析C语言中的volatile关键字

在C语言中,`volatile`关键字用于告诉编译器,程序中存在某些变量,它们的值可能会在程序的控制之外被改变。这通常用于描述硬件寄存器、中断服务例程、多线程共享变量等情况。当编译器遇到一个被声明为`volatile`的变量时,它不会对该变量进行优化。这意味着每次访问该变量时,编译器都会直接从内存中...

java枚举, 什么是枚举

java枚举, 什么是枚举

Java 枚举(Enum)是一种特殊的数据类型,它允许我们定义一组命名的常量。枚举类型通常用于表示一组固定的常量,如星期、月份、状态等。在 Java 中,枚举类型是引用类型,它们被声明为类,但它们有一些特殊的特性。 基本语法```javapublic enum Color { RED, GRE...