当前用于Excel导出常用中间件有:NPOI、 ClosedXML、EPPlus。
NPOI:如SqlSugarCore一样都是国人的开源中件项目,优点也一样:很多示例程序,容易上手,学习曲线也比较平缓。缺点:很久以前本人就过NPOI实现Excel导出,当时由于Excel程序只能打开,通过NPOI中间件自动生成的“.xls”编码格式的Excel文件,如果通过NPOI中间件自动生成的“.xlsx” 编码格式的Excel文件,则Excel程序将不能打该文件,即NPOI中间件只支持自动生成“.xls” 编码格式的Excel文件,而不支持自动生成“.xlsx” 编码格式,到现在为之只少在导出上NPOI(v2.5.6)中间件依然如故。
ClosedXML:是在OpenXml规范上开发的,所它天然支持“.xlsx”编码格式的Excel文件。但是这个命名实在让人吐槽,ClosedXML是对Excel文件进行导入/导出,而不是XML文件,不要它所遵循的规范就命名为“ClosedXML”,另外ClosedXML中间件是一个比较新的中间件,当前版本的迭代的速度也比较快。
EPPlus:功能强大,相对成熟只支持“.xlsx”编码格式的Excel文件的导入/导出,但据说在第4版以后需要购买许可证才能使用(本人未测试,如果有时间会测试,但看到许多开源项目都从EPPlus转到ClosedXML,至具体信息请知道的在评论区留言)
由于需要剖析1个通过NPOI中间件实现Excel导入/导出操作,所以本章特别讲解NPOI,如果开发者要使用本人的推荐优先级:ClosedXML>EPPlus> NPOI
1 重构LogicRender\ Index.cshtml
1.1 添加HTML定义
<button type="button" id="ResetExam" class="btn btn-secondary me-4">
<i class="fa-solid fa-rotate-right">i>
重置
button>
<div class="btn-group">
<button type="button" class="btn btn-success">
<i class="fas fa-download">i>
导出
button>
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
button>
<ul class="dropdown-menu">
<li>
<a id="ExportExamToExcel" class="btn mx-auto bg-success text-white bg-opacity-75">
<i class="far fa-file-excel">i>导出所选到Excel
a>
li>
<li>
<a href="/LogicRender/ExportAllExamToExcel" class="btn mx-auto bg-success text-white bg-opacity-75 mt-1">
<i class="far fa-file-excel">i>导出全部到Excel
a>
li>
ul>
div>
1.2 添加JavaScript定义
//把选中的所有考试成绩导出到Excel文件中。
$("#ExportExamToExcel").on("click", function () {
var idArray = new Array(); //选中的所有考试成绩的ID数组
$("[name=checkItem]:checkbox:checked").each(function (index) {
idArray.push($(this).val());
});
if (idArray.length == 0) {
alert("请选择要导出的考试成绩!");
return;
}
alert("导出考试成绩的编号:" + idArray);
$.post("/LogicRender/ExportExamToExcelAjax/", { IdArray: idArray }, function (data) {
console.log(data.isSuccss);
if (data.isSuccss) {
var pageIndex = tableModel.page();//当前页页码值,但是DataTables插件的页面是从“0”开始,而不从“1”。
$("#example").DataTable().page(pageIndex).draw(false); //刷新当前页;或跳转到指定页。
window.open(data.fileRelativePath);
}
else {
alert(data.message);
}
});
});
2 重构LogicRenderController
2.1 通过AJAX调用导出Excel
/// name="IdArray">数组实例,该实例存储着1/N个整型编号值。
///
/// 【导出所选考试成绩到Excel文件】
///
/// 摘要:
/// 把所选考试成绩,持久化保存到指定Excel文件中。
/// 注意:
/// 该方法通过“FileStream”会将指定Excel文件在服务器端的指定目录(文件夹)中,及其客户端的“下载”目录(文件夹)中各个持久化保存1份。
///
///
/// 返回:
/// Excel文件持久化操作状态的JSON编码数据。
///
///
[HttpPost]
public async Task
{
//获取所以需要被持久化“考试成绩”的所有实例。
ISugarQueryable
.Includes(score => score.CourseSingleton)//1层级联
.Includes(
exam => exam.StudentSingleton,
student => student.SpecialtySingleton)//2层级联
.Includes(
exam => exam.StudentSingleton,
student => student.CategorySingleton)//2层级联
.Includes(
exam => exam.StudentSingleton,
student => student.CategorySingleton,
category => category.GradeSingleton).OrderBy(exam=>exam.Id);//3层级联
List
_exportExamList = await _examQueryable.Where(exam => IdArray.Contains(exam.Id)).ToListAsync();
//通过NPOI中间件,创建Excel文件的对象。
HSSFWorkbook _excelBook = new HSSFWorkbook();
//在当前创建Excel文件,添加一个工作表:“考试成绩”
ISheet _examSheet = _excelBook.CreateSheet("考试成绩");
//结“考试成绩”工作表添加第一行的头部标题
var row1 = _examSheet.CreateRow(0);
row1.CreateCell(0).SetCellValue("编号");
row1.CreateCell(1).SetCellValue("姓名");
row1.CreateCell(2).SetCellValue("学号");
row1.CreateCell(3).SetCellValue("专业");
row1.CreateCell(4).SetCellValue("年级");
row1.CreateCell(5).SetCellValue("班级");
row1.CreateCell(6).SetCellValue("试卷名称");
row1.CreateCell(7).SetCellValue("课程");
row1.CreateCell(8).SetCellValue("成绩");
//将数据依次写入 “考试成绩”工作表各个行
for (var i = 0; i < _exportExamList.Count; i++)
{
var rowtemp = _examSheet.CreateRow(i + 1);
rowtemp.CreateCell(0).SetCellValue(_exportExamList[i].Id.ToString());
rowtemp.CreateCell(1).SetCellValue(_exportExamList[i].StudentSingleton.Name);
rowtemp.CreateCell(2).SetCellValue(_exportExamList[i].StudentSingleton.Code);
rowtemp.CreateCell(3).SetCellValue(_exportExamList[i].StudentSingleton.SpecialtySingleton.Name);
rowtemp.CreateCell(4).SetCellValue(_exportExamList[i].StudentSingleton.CategorySingleton.GradeSingleton.Name);
rowtemp.CreateCell(5).SetCellValue(_exportExamList[i].StudentSingleton.CategorySingleton.Name);
rowtemp.CreateCell(6).SetCellValue(_exportExamList[i].Name);
rowtemp.CreateCell(7).SetCellValue(_exportExamList[i].CourseSingleton.Name);
if (_exportExamList[i].Score != null)
{
rowtemp.CreateCell(8).SetCellValue(_exportExamList[i].Score.ToString());
}
else
{
rowtemp.CreateCell(8).SetCellValue(_exportExamList[i].Comment);
}
}
//注意:Excel程序只能打,通过NPOI中间件自动生成的“.xls”格式的Excel文件,如果通过NPOI中间件自动生成的“.xlsx”格式的Excel文件,Excel程序将不能打该文件
//即NPOI中间件只支持自动生成“.xls”格式的Excel文件,而不支持自动生成“.xlsx”格式的Excel文件。
//Excel导出文件被持久化保存在服务器端目录(文件夹)中的相对路径。
string _hostRelativeDirectory = "/Excel导出/" + DateTime.Now.ToString("yyyy-MM-dd") + "/";
//当前被持久化保存的Excel文件的文件名。
string _excelFileName = "成绩单_选择结果(" + DateTime.Now.ToString("HHmmssfff") + ").xls";
string fileRelativePath = _hostRelativeDirectory + _excelFileName;//fileRelativePath变量要回传给:AJax方法。
//获取服务器端中当前程序的“wwwroot”目录(文件夹)的绝对路径。
string _webRootPath = _webHostEnvironment.WebRootPath;
//Excel导出文件被持久化保存在服务器端目录(文件夹)中的绝对路径。
string _hostAbsoluteDirectory = _webRootPath + _hostRelativeDirectory;
//如果指定绝对路径中没有该目录(文件夹)不存在,则新建该目录(文件夹)。
DirectoryInfo _directoryInfo = new DirectoryInfo(_hostAbsoluteDirectory);
if (!_directoryInfo.Exists)
_directoryInfo.Create();
//当前Excel文件被持久化保存的绝对路径。
string _fileAbsolutePath = _hostAbsoluteDirectory + _excelFileName;
try
{
// 通过文件流实例,把内存中的数据持久化保存到服务器端的指定目录(文件夹)中,并在持久化完成后,最后通过文件流实例的关闭方法,来自动销毁和释放内存中的数据。
FileStream _fileStream = new FileStream(_fileAbsolutePath, FileMode.Create);
_excelBook.Write(_fileStream);
_fileStream.Flush();
_fileStream.Close();
return Json(new { isSuccss = true, message = "Excel导出成功", fileRelativePath });
}
catch (Exception exception)
{
if (_excelBook != null)
_excelBook.Close();
if (System.IO.File.Exists(_fileAbsolutePath))
System.IO.File.Delete(_fileAbsolutePath);
Console.Write(exception.Message);
return Json(new { isSuccss = false, message = "Excel导出失败", string.Empty });
}
}
2.2 通过路由调用导出Excel
///
/// 【导出所选考试成绩到Excel文件】
///
/// 摘要:
/// 把所有考试成绩,持久化保存到指定Excel文件中。
/// 注意:
/// 该方法通过“ControllerBase.File”直接把内存中指定Excel文件持久化保存到客户端的“下载”目录(文件夹)中; 服务器端不在持久化保存该Excel文件。
///
///
/// 返回:
/// 把内存中指定Excel文件持久化保存到客户端的“下载”目录(文件夹)中后,打开。
///
///
public async Task
{
//获取“考试成绩”的所有实例。
List
.Includes(score => score.CourseSingleton)//1层级联
.Includes(
exam => exam.StudentSingleton,
student => student.SpecialtySingleton)//2层级联
.Includes(
exam => exam.StudentSingleton,
student => student.CategorySingleton)//2层级联
.Includes(
exam => exam.StudentSingleton,
student => student.CategorySingleton,
category => category.GradeSingleton)//3层级联
.OrderBy(exam => exam.Id)
.ToListAsync();
//通过NPOI中间件,创建Excel文件的对象。
HSSFWorkbook _excelBook = new HSSFWorkbook();
//在当前创建Excel文件,添加一个工作表:“考试成绩”
ISheet _examSheet = _excelBook.CreateSheet("考试成绩");
//结“考试成绩”工作表添加第一行的头部标题
var row1 = _examSheet.CreateRow(0);
row1.CreateCell(0).SetCellValue("编号");
row1.CreateCell(1).SetCellValue("姓名");
row1.CreateCell(2).SetCellValue("学号");
row1.CreateCell(3).SetCellValue("专业");
row1.CreateCell(4).SetCellValue("年级");
row1.CreateCell(5).SetCellValue("班级");
row1.CreateCell(6).SetCellValue("试卷名称");
row1.CreateCell(7).SetCellValue("课程");
row1.CreateCell(8).SetCellValue("成绩");
//将数据依次写入 “考试成绩”工作表各个行
for (var i = 0; i < _exportExamList.Count; i++)
{
var rowtemp = _examSheet.CreateRow(i + 1);
rowtemp.CreateCell(0).SetCellValue(_exportExamList[i].Id.ToString());
rowtemp.CreateCell(1).SetCellValue(_exportExamList[i].StudentSingleton.Name);
rowtemp.CreateCell(2).SetCellValue(_exportExamList[i].StudentSingleton.Code);
rowtemp.CreateCell(3).SetCellValue(_exportExamList[i].StudentSingleton.SpecialtySingleton.Name);
rowtemp.CreateCell(4).SetCellValue(_exportExamList[i].StudentSingleton.CategorySingleton.GradeSingleton.Name);
rowtemp.CreateCell(5).SetCellValue(_exportExamList[i].StudentSingleton.CategorySingleton.Name);
rowtemp.CreateCell(6).SetCellValue(_exportExamList[i].Name);
rowtemp.CreateCell(7).SetCellValue(_exportExamList[i].CourseSingleton.Name);
if (_exportExamList[i].Score != null)
{
rowtemp.CreateCell(8).SetCellValue(_exportExamList[i].Score.ToString());
}
else
{
rowtemp.CreateCell(8).SetCellValue(_exportExamList[i].Comment);
}
}
//注意:Excel程序只能打,通过NPOI中间件自动生成的“.xls”格式的Excel文件,如果通过NPOI中间件自动生成的“.xlsx”格式的Excel文件,Excel程序将不能打该文件
//即NPOI中间件只支持自动生成“.xls”格式的Excel文件,而不支持自动生成“.xlsx”格式的Excel文件。
//当前被持久化保存的Excel文件的文件名。
string _excelFileName = "所有成绩(" + DateTime.Now.ToString("HHmmssfff") + ").xls";
MemoryStream _memoryStream = new MemoryStream();
try
{
_excelBook.Write(_memoryStream);
byte[] data = _memoryStream.ToArray();
//通过“ControllerBase.File”直接把内存中指定Excel文件持久化保存到客户端的“下载”目录(文件夹)中; 服务器端不在持久化保存该Excel文件。
return File(data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", _excelFileName);
}
catch (Exception exception)
{
if (_excelBook != null)
_excelBook.Close();
if (_memoryStream != null)
{
_memoryStream.Close();
_memoryStream.Dispose();
}
Console.Write(exception.Message);
return RedirectToAction("Index");
}
}
对以上功能更为具体实现和注释见:22-10-20-10_SqlSugarAcquaintance(初识SqlSugarCore之NPOI Excel导出)。