.Net Core Excel导入导出神器Npoi.Mapper

关于 Npoi.Mapper 看名字我们就知道,它并不是一款创新型的软件,而是针对Npoi的二次封装增强了关于Mapper相关的操作。秉承着使用非常简单的原则,不过这样能够满足我们日常开发工作中很大一部分应用场景。

保存文件

常规操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
  • overwrite参数如果是要覆盖已存在的Excel或者新建Excel则为true,如果在原有Excel上追加数据则为false,
  • xlsx参数是用于区分导出的Excel格式为xlsx还是xls。

定义列名和格式

1
2
3
4
5
6
7
8
9
10
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);

1
2
3
4
5
6
7
8
9
10
11
public class Student
{
[Column("学号",CustomFormat = "0%")]
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; }
}

导出到多个sheet中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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= 18060006000},
new Person{ Id = 2,Name="李四", Tel = 18060006000}
};
var mapper = new Mapper();
//放入Mapper中
//第一个参数是数据集合,第二个参数是Sheet名称,第三个参数表示是追加数据还是覆盖数据
mapper.Put<Student>(students, "student",true);
mapper.Put<Person>(persons, "person",true);
mapper.Save("Human.xlsx");

下载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[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");
}

导入操作

常规操作

1
2
3
4
5
6
7
8
9
10
//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}]");
}

动态类型

1
2
3
4
5
6
7
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}]");
}

上传导入

1
2
3
4
5
6
7
8
9
10
[HttpPost] 
public IEnumerable<Student> UploadFile(IFormFile formFile)
{
//通过上传文件流初始化Mapper
var mapper = new Mapper(formFile.OpenReadStream());
//读取sheet1的数据
return mapper.Take<Student>(a,n).Select(i=>i.Value);
return mapper.Take<Student>(b,n).Select(i=>i.Value);
// a = sheet名称 b = sheet索引 n = 开始读取行
}

扩展属性

自定义列宽

源码没有,自己通过源码添加的功能

  • 单个中文长度 = 2
  • 单个非中文长度 = 1
1
2
3
// 自定义宽度
[Column(Width = 10)]
public string IgnoredProperty { get; set; }

忽略操作

1
2
3
// 忽略操作
[Ignore]
public string IgnoredProperty { get; set; }

数字格式化

1
2
3
4
5
6
7
// 四舍五入,不足补0
[Column(CustomFormat = "0.000")]
public double IgnoredProperty { get; set; }

// 四舍五入,不足补0,带符号
[Column(CustomFormat = "0.000%")]
public double IgnoredProperty { get; set; }

合并单元格

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

1
2
3
// 合并单元格
[UseLastNonBlankValue]
public string ClassName { get; set; }

源码中的合并似乎有点问题
修改源码后默认判断所有单元格都判断合并并通过纬度判断区域

自定义Map规则

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

1
2
3
4
5
6
7
8
9
/// <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里的性别数据映射到实体中的时候做一下中英文的处理,就可以使用以下操作

1
2
3
4
5
6
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”。总之你想处理一下,自定义映射逻辑都可以使用这个功能。