前言

我们在日常开发中对Excel的操作可能会比较频繁,好多功能都会涉及到Excel的操作。在.Net Core中大家可能使用Npoi比较多,这款软件功能也十分强大,而且接近原始编程。但是直接使用Npoi大部分时候我们可能都会自己封装一下,毕竟根据二八原则,我们百分之八十的场景可能都是进行简单的导入导出操作,这里就引出我们的主角Npoi.Mapper了。

简介

关于Npoi.Mapper看名字我们就知道,它并不是一款创新型的软件,而是针对Npoi的二次封装增强了关于Mapper相关的操作。秉承着使用非常简单的原则,不过这样能够满足我们日常开发工作中很大一部分应用场景。它的GitHub地址为https://github.com/donnytian/Npoi.Mapper,目前Star并不多才240多,但是确实是非常好用,这里强烈推荐一波。接下来我们就大概演示一下的它的使用。

常规操作

Npoi.Mapper的主题内容包括两大块,一个是针对导入,一个是针对导出。接下来我们先来简单演示一下最基础的导入导出。首先我们新建一个Student类作为数据承载的载体,简单定义大致如下

public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
public DateTime BirthDay { get; set; }
}

然后引入Npoi.Mapper的nuget包

<PackageReference Include="Npoi.Mapper" Version="3.5.1" />
导出操作

接下来我们构建一个Student集合,然后初始化一部分简单的数据,将这些数据导出到Excel,接下来做一个简单的演示

static void Main(string[] args)
{
List<Student> students = new List<Student>
{
new Student{ Id = 1,Name="夫子",Sex="男",BirthDay=new DateTime(1999,10,11) },
new Student{ Id = 2,Name="余帘",Sex="女",BirthDay=new DateTime(1999,12,12) },
new Student{ Id = 3,Name="李慢慢",Sex="男",BirthDay=new DateTime(1999,11,11) },
new Student{ Id = 4,Name="叶红鱼",Sex="女",BirthDay=new DateTime(1999,10,10) }
};
//声明mapper操作对象
var mapper = new Mapper();
//第一个参数为导出Excel名称
//第二个参数为Excel数据来源
//第三个参数为导出的Sheet名称
//overwrite参数如果是要覆盖已存在的Excel或者新建Excel则为true,如果在原有Excel上追加数据则为false
//xlsx参数是用于区分导出的数据格式为xlsx还是xls
mapper.Save("Students.xlsx", students, "sheet1", overwrite: true, xlsx:true);
Console.WriteLine("执行完成");
}

其中overwrite参数如果是要覆盖已存在的Excel或者新建Excel则为true,如果在原有Excel上追加数据则为false,说白了就是控制是新建Excel文件还是在原有基础上直接追加。xlsx参数是用于区分导出的Excel格式为xlsx还是xls。通过上述简单代码便可以实现Excel的导出功能,真的是非常简单,如果你只是进行简单的导出操作,通过Npoi.Mapper操作真的是不二的选择。这样导出的Excel效果如下所示

但是这样导出的Excel头信息为属性的名称,而且我们Student类中包含了一个时间字段BirthDay为DateTime类型,这个表示格式好像也不太符合我们常规的阅读习惯,那该怎么办呢?Npoi.Mapper为我们提供了两种处理方式,一种是通过Fluent的方式指定映射关系如下所示

var mapper = new Mapper();
//第一个参数表示导出的列名,第二个表示对应的属性字段
mapper.Map<Student>("姓名", s => s.Name)
.Map<Student>("学号", s => s.Id)
.Map<Student>("性别", s => s.Sex)
.Map<Student>("生日", s => s.BirthDay)
//格式化操作,第一个参数表示格式,第二表示对应字段
//Format不仅仅只支持时间操作,还可以是数字或金额等
.Format<Student>("yyyy-MM-dd", s => s.BirthDay);
mapper.Save("Students.xlsx", students, "sheet1", overwrite: true, xlsx:true);

经过上面相关操作之后导出后的效果如下所示还有一种形式是通过ColumnAttribute的形式在导出的实体类的属性上进行声明导出列相关设置,具体操作如下

public class Student
{
[Column("学号")]
public int Id { get; set; }
[Column("姓名")]
public string Name { get; set; }
[Column("性别")]
public string Sex { get; set; }
[Column("生日",CustomFormat = "yyyy-MM-dd")]
public DateTime BirthDay { get; set; }
}

通过这种方式操作和通过Fluent的效果是完全一样的,至于使用哪一种完全看个人喜好,不过我个人更喜欢在属性上直接声明的方式,这样看起来显得一目了然。

有时候我们可能需要将不同的数据源导入到同一个Excel的不同Sheet中,Npoi.Mapper也提供了这方面的支持,具体操作方式如下所示

static void Main(string[] args)
{
//构建Student集合
List<Student> students = new List<Student>
{
new Student{ Id = 1,Name="夫子",Sex="男",BirthDay=new DateTime(1999,10,11) },
new Student{ Id = 2,Name="余帘",Sex="女",BirthDay=new DateTime(1999,12,12) }
};
//构建Person集合
List<Person> persons = new List<Person>
{
new Person{ Id = 1,Name="陈某", Tel= 18833445566},
new Person{ Id = 2,Name="柯浩然", Tel = 15588997766}
};
var mapper = new Mapper();
//放入Mapper中
//第一个参数是数据集合,第二个参数是Sheet名称,第三个参数表示是追加数据还是覆盖数据
mapper.Put<Student>(students, "student",true);
mapper.Put<Person>(persons, "person",true);
mapper.Save("Human.xlsx");
}

不过很多时候我们是通过Web程序直接将数据转换为文件流返回的,并不会生成Excel文件,Npoi.Mapper很贴心的为我们提供了将数据读取到Stream的操作,操作方式如下

[HttpGet]
public ActionResult DownLoadFile()
{
List<Student> students = new List<Student>
{
new Student{ Id = 1,Name="夫子",Sex="男",BirthDay=new DateTime(1999,10,11) },
new Student{ Id = 2,Name="余帘",Sex="女",BirthDay=new DateTime(1999,12,12) },
new Student{ Id = 3,Name="李慢慢",Sex="男",BirthDay=new DateTime(1999,11,11) },
new Student{ Id = 4,Name="叶红鱼",Sex="女",BirthDay=new DateTime(1999,10,10) }
}; var mapper = new Mapper();
MemoryStream stream = new MemoryStream();
//将students集合生成的Excel直接放置到Stream中
mapper.Save(stream, students, "sheet1", overwrite: true, xlsx: true);
return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","Student.xlsx");
}

Save提供了几个重载方法,其中有一个就是将数据保存到Stream中,但是这里也踩到了一个坑,不过这个是Npoi的坑并不是Npoi.Mapper的坑,那就是Workbook.Write(stream)的时候会将stream关闭,如果继续操作这个Stream会报流已关闭的错误,而Npoi.Mapper的Save到Stream的方法恰恰是对这个方法的封装,这也是为何上面我没直接在File中直接返回Stream,而是将其转换为byte数组再返回的原因。

导入操作

上面我们演示了使用Npoi.Mapper将数据导出的场景,接下来我们来演示通过Npoi.Mapper的读取Excel的相关操作,操作也是非常的简单,话不多说直接上代码,比如我读取上面导出的Excel

//Excel文件的路径
var mapper = new Mapper("Students.xlsx");
//读取的sheet信息
var studentRows = mapper.Take<Student>("sheet1");
foreach (var row in studentRows)
{
//映射的数据保留在value中
Student student = row.Value;
Console.WriteLine($"姓名:[{student.Name}],学号:[{student.Id}],性别:[{student.Sex}],生日:[{student.BirthDay:yyyy-MM-dd}]");
}

通过Take方法直接读取出来的是RowInfo集合,RowInfo是用来包装读取数据的包装类。通过它可以获取读取的行号,或读取过程中可能会出现异常情况,比如某一列读取失败,它会将列信息和报错信息记录下来,如果你不需要这些信息或者觉得遍历的时候比较麻烦想直接拿到需要的集合,可以通过如下方式转换一下

var studentRows = mapper.Take<Student>("sheet1");
//通过lambda获取到Student集合
var students = studentRows.Select(i => i.Value);

有的时候你可能不想定义一个POCO去接收返回的结果,而是想直接拿到读取信息,转换成你需要的数据格式。比如你想读取Excel中的数据,将结果转换为实体类直接入库,但是你不想定义一个专门的映射类去接收读取结果,这时候你需要一个动态类型去接收,而Npoi.Mapper恰恰提供了这样的功能,可以将Excel中的数据直接读取到dynamic中去,具体操作和上面类似

var mapper = new Mapper("Students.xlsx");
var studentRows = mapper.Take<dynamic>("sheet1");
foreach (var row in studentRows)
{
var student = row.Value;
Console.WriteLine($"姓名:[{student.姓名}],学号:[{student.学号}],性别:[{student.性别}],生日:[{student.生日:yyyy-MM-dd}]");
}

其中你要操作的字段名称和Excel的列名是一致的,比如我的Excel列名叫姓名,那么我读取的时候对应的属性名称也叫姓名。

同样的情况也存在于导入操作,比如许多情况下我们是通过Web接口直接上传的文件,这种场景下,我们通常能拿到上传的流信息,Npoi.Mapper也支持读取Excel文件流的形式获取Excel数据,如下所示

[HttpPost]
public IEnumerable<Student> UploadFile(IFormFile formFile)
{
//通过上传文件流初始化Mapper
var mapper = new Mapper(formFile.OpenReadStream());
//读取sheet1的数据
return mapper.Take<Student>("sheet1").Select(i=>i.Value);
}

其他功能

除了上面介绍的主要功能之外Npoi.Mapper还提供了一些其他的功能,简单介绍一下几个比较实用的点

忽略操作

有时候我们的导出或导入数据可能想忽略某些列不导出,Npoi.Mapper为了我们提供了类似EF的Ignore操作

[Ignore]
public string IgnoredProperty { get; set; }

这样的话无论是导入还是导出都会忽略这个属性,即导出不会显示这个列,导入不会映射这一列的数据

合并单元格

如果我们导入的数据有一列数据的值是大家都拥有的,在Excel上可以通过合并单元格的操作来显示这一列,对于合并单元格的列,对于程序来讲就是等价于所有列都是同一个值,Npoi.Mapper为我们做了这种处理

[UseLastNonBlankValue]
public string ClassName { get; set; }
自定义Map规则

虽然默认情况下Npoi.Mapper能帮我们满足大部分的类型映射关系,但是有时候我们需要根据我们自己的规则处理处理数据映射关系,这时候我们需要用到Map功能,他有许多重载的方法,我们就查看一个比较常用的方法做参数讲解

/// <param name="columnName">对应Excel列的名称</param>
/// <param name="propertyName">对应实体的属性名称</param>
/// <param name="tryTake">该函数用于处理从Excel读取时针对单元格数据的处理</param>
/// <param name="tryPut">该函数用于处理将数据导出到Excel是针对源数据的处理</param>
public static Mapper Map<T>(this Mapper mapper, string columnName, string propertyName,
Func<IColumnInfo, object, bool> tryTake = null,
Func<IColumnInfo, object, bool> tryPut = null)
{
}

其中tryTake用于处理从Excel导出时针对单元格数据的处理,IColumnInfo代表数据的来源,object代表对应将Row导入到某个实体中。tryPut恰恰相反,用于处理将数据导出到Excel是针对源数据的处理。其中IColumnInfo代表要导出到的列信息,object代表数据的源。简单演示一下,比如我想将上述示例中,读取到Excel里的性别数据映射到实体中的时候做一下中英文的处理,就可以使用以下操作

var mapper = new Mapper("Students.xlsx");
mapper.Map<Student>("性别", "Sex", (c, t) => {
Student student = t as Student;
student.Sex = c.CurrentValue == "男" ? "MAN" : "WOMAN";
return true;
}, null);

因为我是要读取Excel,所以使用tryTake函数,t代表target表示要映射到的实体,c代表读取到的单元格信息,我将读取到target里的数据做一下处理,如果在单元格中读取的是"男"那么对应到Student转换为"MAN",反之则为"WOMAN"。总之你想处理一下,自定义映射逻辑都可以使用这个功能。

总结

以上是我们对Npoi.Mapper的大致讲解,我个人还是非常推荐的。它的使用足够简单而且功能非常完善,因为它既可以处理Excel导入操作,也可以处理Excel导出操作。它很强大,因为它可以满足我们日常开发中,大部分关于导入导出Excel的场景。但是它还不够强大,因为它还存在一定的缺陷,而且许多细节可能还没考虑到。不过庆幸的是,它的源码非常的简单一共不到20个类,而且逻辑非常清晰。如果有的情况它真的不能满足,我们完全可以下载它的源码自己扩展操作。最后再次贴上它的GitHub地址https://github.com/donnytian/Npoi.Mapper如果大家有类似的场景可以尝试使用一下。

欢迎扫码关注我的公众号

.Net Core Excel导入导出神器Npoi.Mapper的更多相关文章

  1. .net core excel导入导出

    做的上一个项目用的是vs2013,传统的 Mvc模式开发的,excel报表的导入导出都是那几段代码,已经习惯了. 导入:string filename = ExcelFileUpload.FileNa ...

  2. c#.Net:Excel导入/导出之NPOI 2.0简介

      NPOI 2.0+主要由SS, HPSF, DDF, HSSF, XWPF, XSSF, OpenXml4Net, OpenXmlFormats组成,具体列表如下: 资料来自:百度百科   Ass ...

  3. 基于EPPlus和NPOI实现的Excel导入导出

    基于EPPlus和NPOI实现的Excel导入导出 CollapseNav.Net.Tool.Excel(NuGet地址) 太长不看 导入 excel 文件流将会转为 ExcelTestDto 类型的 ...

  4. Excel导入导出帮助类

    /// <summary>    /// Excel导入导出帮助类    /// 记得引入 NPOI    /// 下载地址   http://npoi.codeplex.com/rele ...

  5. Octopus——excel导入导出工具

    Octopus Octopus是一个简易的Excel导入导出工具.目前主要就两个功能: 导入:将excel中一行数据转换为指定的java对象,并通过指定的正则表达式检查合法性. 导出:按照给定的xml ...

  6. java简易excel导入导出工具(封装POI)

    Octopus 如何导入excel 如何导出excel github项目地址 Octopus Octopus 是一个简单的java excel导入导出工具. 如何导入excel 下面是一个excel文 ...

  7. 利用反射实现通用的excel导入导出

    如果一个项目中存在多种信息的导入导出,为了简化代码,就需要用反射实现通用的excel导入导出 实例代码如下: 1.创建一个 Book类,并编写set和get方法 package com.bean; p ...

  8. Excel导入导出的业务进化场景及组件化的设计方案(上)

    1:前言 看过我文章的网友们都知道,通常前言都是我用来打酱油扯点闲情的. 自从写了上面一篇文章之后,领导就找我谈话了,怕我有什么想不开. 所以上一篇的(下)篇,目前先不出来了,哪天我异地二次回忆的时候 ...

  9. 关于Excel导入导出的用例设计

    目前,为方便操作,很多系统都会增加批量导入导出的功能.文件导入导出一般格式都是excel.由于用户直接在excel在填写内容,无法控制填写的格 式,加上excel解析比较困难,所以一般涉及到excel ...

随机推荐

  1. Python博文_爬虫工程师是干什么的

    程序员有时候很难和外行人讲明白自己的工作是什么,甚至有些时候,跟同行的人讲清楚"你是干什么的"也很困难.比如我自己,就对Daivd在搞的语义网一头雾水.所以我打算写一篇博客,讲一下 ...

  2. php socket 编程问题总结

    1. PHP declare(ticks=N); 的作用 https://blog.csdn.net/udefined/article/details/24333333 2. pack &&a ...

  3. Python相比其他计算机语言真的更有优势吗?

    要了解Python相比其他计算机语言,首先要了解Python语言的特点 Python语言的特点 一.简单易学.明确优雅.开发速度快 ①简单易学:与 C 和 Java 比,Python的学习成本和难度曲 ...

  4. pandas 对时间索引进行分割

    截取最近1个月时间,截取最近一段时间,进行统计分析 df.loc["2016-01-05":"2016-02-05",:].tail() 在index为有序数据 ...

  5. Guitar Pro 教程之有效的几种泛音

    学习音乐的朋友们都知道,泛音的种类分为好几种,一般分为自然泛音和人工泛音,击弦泛音,拨片泛音,半泛音等等.本章节采用图文结合的方式讲解{cms_selflink page='index' text=' ...

  6. Linux安装禅道教程

    环境: centos7 64位 禅道11.2 Linux一键安装包64位 下载: 禅道下载地址: http://dl.cnezsoft.com/zentao/11.2/ZenTaoPMS.11.2.s ...

  7. GitHub 上 1.3k Star 的 strman-java 项目有值得学习的地方吗?源码视角

    大家好,我是沉默王二. 很多初学编程的同学,经常给我吐槽,说:"二哥,你在敲代码的时候会不会有这样一种感觉,写着写着看不下去了,觉得自己写出来的代码就好像屎一样?" 这里我必须得说 ...

  8. 为什么90%的大学都要求计算机专业学习C语言?

    编程语言是编程的工具,计算机相关专业的学生必须具备足够的编程能力.当然,关于"最好语言"的争论从来没有休止过,这里要强调一下:语言的选择真的没那么重要,学习语言的过程最重要是语言的 ...

  9. 浅谈JAVA servlet

    1.servlet是什么? servlet的本质是接口,接口就是一种规范.我们来看一下servlet接口中都有哪些函数: 图片来源:https://www.cnblogs.com/whgk/p/639 ...

  10. CentOS下如何用nmon收集系统实时运行状况

    #赋予执行权限 chmod +x nmon 执行./nmon可以查看实时的系统状态有提示的,d看磁盘,n看网络,c看cpu #如果不想看实时的,想收集系统长时间运行情况然后分析,可用这个 nohup ...